输入关键词开始搜索

C++ 智能指针

为什么需要智能指针

// ❌ 裸指针:容易忘记 delete → 内存泄漏
void foo() {
    int *p = new int(42);
    // ... 抛异常 → delete 永远不执行 → 泄漏
    delete p;
}

// ✅ 智能指针:自动释放,异常安全
void foo() {
    auto p = make_unique<int>(42);
    // 离开作用域自动 delete,抛异常也释放
}

核心思想:RAII(Resource Acquisition Is Initialization)——资源获取即初始化,对象析构时自动释放资源。

三种智能指针

指针所有权场景
unique_ptr独占工厂函数、成员变量、PIMPL
shared_ptr共享多对象共享同一资源、图/树结构
weak_ptr旁观打破循环引用、缓存

unique_ptr — 独占

#include <memory>

// 创建(推荐 make_unique,C++14)
auto p1 = make_unique<int>(42);
auto p2 = make_unique<string>("hello");

// 不可拷贝,只能移动
auto p3 = move(p1);     // p1 变为 nullptr,p3 接管所有权

// 工厂函数:返回 unique_ptr
unique_ptr<Base> createFactory() {
    return make_unique<Derived>();  // 自动转换为基类指针
}

// 自定义删除器
auto deleter = [](FILE *f) { fclose(f); };
unique_ptr<FILE, decltype(deleter)> fp(fopen("test.txt", "r"), deleter);

规则:能用 unique_ptr 就别用 shared_ptr。独占所有权覆盖 80% 场景。


shared_ptr — 共享

auto sp1 = make_shared<int>(42);   // 引用计数 = 1
auto sp2 = sp1;                    // 引用计数 = 2
sp2.reset();                       // 引用计数 = 1
// sp1 离开作用域 → 引用计数 = 0 → delete

// 观察引用计数
cout << sp1.use_count();           // 1

// 从 this 获取 shared_ptr(继承 enable_shared_from_this)
class Node : public enable_shared_from_this<Node> {
public:
    shared_ptr<Node> getPtr() {
        return shared_from_this();
    }
};

代价

  • 控制块额外 16 字节
  • 引用计数是原子操作,有开销
  • make_shared 一次分配(对象+控制块),new + shared_ptr 两次分配

weak_ptr — 旁观

auto sp = make_shared<int>(42);
weak_ptr<int> wp = sp;

// 使用时必须 lock() — 检查对象是否还活着
if (auto locked = wp.lock()) {
    cout << *locked;    // 安全访问
} else {
    cout << "已被释放";
}

sp.reset();             // 引用计数归零
// wp.lock() 返回 nullptr

经典场景:打破循环引用

class B;
class A {
public:
    shared_ptr<B> b_ptr;  // A 持有 B
    ~A() { cout << "A destroyed\n"; }
};
class B {
public:
    // shared_ptr<A> a_ptr;  // ❌ 互相持有 → 永不释放
    weak_ptr<A> a_ptr;       // ✅ 弱引用 → A 可以正常析构
    ~B() { cout << "B destroyed\n"; }
};

选择决策树

需要多个持有者共享所有权?
  ├─ 是 → shared_ptr
  │       └─ 有循环引用风险?
  │            └─ 是 → 其中一方用 weak_ptr
  └─ 否 → unique_ptr
           └─ 需要自定义删除器 → unique_ptr<T, Deleter>