c++ 智能指针

1.概要

2.内容

C++ 智能指针是 C++11 引入的现代内存管理工具,用于自动化管理动态分配的内存,避免内存泄漏和悬垂指针问题。以下是智能指针的核心知识点和用法指南:

一、智能指针类型

  1. unique_ptr(独占所有权)

    • 特点:同一时间只能有一个 unique_ptr 拥有对象所有权
    • 用途:替代裸指针管理独占资源

    std::unique_ptr<int> ptr(new int(42));
    // 转移所有权
    std::unique_ptr<int> ptr2 = std::move(ptr);

  2. shared_ptr(共享所有权)

    • 特点:通过引用计数实现多指针共享同一对象
    • 注意:循环引用会导致内存泄漏

    auto ptr1 = std::make_shared<int>(10);
    auto ptr2 = ptr1; // 引用计数+1

  3. weak_ptr(弱引用)

    • 特点:不增加引用计数,用于解决 shared_ptr 循环引用
    std::weak_ptr<int> weak = ptr1;
    if (auto tmp = weak.lock()) { // 检查有效性
        // 安全使用 *tmp
    }

二、核心优势

  • 自动内存回收:离开作用域自动释放内存
  • 异常安全:无需手动 delete
  • 明确所有权语义:编译期检查所有权规则

三、最佳实践

  1. 优先使用 make_shared/make_unique(C++14 起)

    auto ptr = std::make_shared<MyClass>(arg1, arg2);

    避免裸指针与智能指针混用

    // 错误:可能导致重复释放
    std::shared_ptr<int> p1(new int(5));
    int* p2 = p1.get();
    delete p2; // 危险!

    循环引用解决方案

    // 错误:可能导致重复释放
    std::shared_ptr<int> p1(new int(5));
    int* p2 = p1.get();
    delete p2; // 危险!
  2. 自定义删除器

    // 文件指针示例
    std::unique_ptr<FILE, decltype(&fclose)> file_ptr(
        fopen("test.txt", "r"), 
        fclose
    );

四、性能注意

  • shared_ptr 有额外开销(控制块、原子操作)
  • unique_ptr 性能接近裸指针
  • 循环引用检测工具:Valgrind、AddressSanitizer

智能指针是现代 C++ 资源管理的基石,合理使用能显著提升代码安全性和可维护性。建议新项目优先使用智能指针,仅在必要时使用裸指针(如与 C 接口交互)。

3.关联链接

4.关联知识 

1.c++ 智能指针使用场景举例 

智能指针的核心优势在于明确所有权语义和自动内存管理,以下是针对不同场景的智能指针使用指南及代码示例:

一、unique_ptr 适用场景

特点:独占所有权,不可复制,只能移动
适用场景

  1. 工厂模式(转移所有权)

std::unique_ptr<Image> createImage(int type) {
    if (type == 1) return std::make_unique<JpegImage>();
    else return std::make_unique<PngImage>();
}

// 使用方
auto img = createImage(1); // 所有权转移
  1. 容器存储动态对象

std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Square>());
  1. 资源管理(文件/网络句柄)

void processFile() {
    auto file = std::unique_ptr<FILE, decltype(&fclose)>(
        fopen("data.txt", "r"), 
        fclose
    );
    if (!file) throw std::runtime_error("Open failed");
    // 使用文件...
} // 自动关闭文件

二、shared_ptr 适用场景

特点:共享所有权,引用计数管理
适用场景

  1. 共享缓存系统

class ImageCache {
public:
    std::shared_ptr<Image> getImage(const std::string& id) {
        // 从缓存或加载新图像
        return std::shared_ptr<Image>(new Image(id), [this](Image* p){
            // 自定义删除逻辑(如从缓存移除)
        });
    }
};

// 多个组件共享图像
auto img1 = cache.getImage("logo");
auto img2 = img1; // 引用计数+1
  1. 观察者模式

class Subject {
    std::vector<std::weak_ptr<Observer>> observers;
public:
    void attach(std::shared_ptr<Observer> obs) {
        observers.push_back(obs);
    }
    void notify() {
        for (auto& wp : observers) {
            if (auto sp = wp.lock()) {
                sp->update();
            }
        }
    }
};

三、weak_ptr 适用场景

特点:不控制生命周期,用于观察 shared_ptr
适用场景

  1. 解决循环引用

class Parent;
class Child {
    std::weak_ptr<Parent> parent; // 弱引用
public:
    void setParent(std::shared_ptr<Parent> p) {
        parent = p;
    }
};

class Parent {
    std::shared_ptr<Child> child;
public:
    void setChild(std::shared_ptr<Child> c) {
        child = c;
        c->setParent(shared_from_this()); // 需要 enable_shared_from_this
    }
};
  1. 缓存临时访问

class Parent;
class Child {
    std::weak_ptr<Parent> parent; // 弱引用
public:
    void setParent(std::shared_ptr<Parent> p) {
        parent = p;
    }
};

class Parent {
    std::shared_ptr<Child> child;
public:
    void setChild(std::shared_ptr<Child> c) {
        child = c;
        c->setParent(shared_from_this()); // 需要 enable_shared_from_this
    }
};

四、选择策略总结

智能指针适用场景典型特征
unique_ptr独占所有权、性能敏感场景不可复制,零额外开销
shared_ptr共享所有权、需要自动回收引用计数,线程安全
weak_ptr观察共享对象、打破循环引用不影响引用计数,需配合 lock()

实际开发中:

  1. 优先使用 unique_ptr 作为默认选择
  2. 需要共享所有权时才使用 shared_ptr
  3. 用 weak_ptr 作为 shared_ptr 的观察者或解决循环依赖
  4. 避免混合使用裸指针和智能指针管理同一对象

2.unique_ptr 我有个类A 如何用unique_ptr创建一个A的指针 

使用 unique_ptr 创建类 A 的指针有两种推荐方式,以下是具体示例:

#include <memory>

class A {
public:
    void doSomething() { /* ... */ }
    int value = 42;
};

int main() {
    // 方式1:使用 make_unique (推荐,C++14起)
    auto ptr1 = std::make_unique<A>();
    ptr1->doSomething();
    ptr1->value = 100;

    // 方式2:直接构造 unique_ptr
    std::unique_ptr<A> ptr2(new A());
    ptr2->value = 200;

    // 所有权转移示例
    auto ptr3 = std::move(ptr1); // ptr1变为nullptr
    ptr3->doSomething();

    return 0;
}

关键说明:

  1. std::make_unique<A>() 是首选方式(C++14及以上),因为它:
    • 自动推导类型,避免显式写 new
    • 异常安全(若构造函数抛出异常,内存不会泄漏)
    • 代码更简洁
  2. 直接构造方式 std::unique_ptr<A>(new A()) 适用于:
    • 需要传递自定义删除器
    • 需要使用已有的裸指针初始化
  3. 访问成员:
    • 使用箭头运算符 -> 访问成员函数/变量
    • 也可以通过 ptr.get() 获取原始指针(谨慎使用)

最佳实践:

  • 优先使用 make_unique
  • 避免混合使用 unique_ptr 和裸指针管理同一对象
  • 需要转移所有权时使用 std::move()
  • 通过 if (ptr) 检查指针是否为空

注意:如果编译器不支持 C++14,需使用方式2,但建议升级编译器以获得更好的安全性。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值