4
RAII 与智能指针:C++ 最重要的范式
RAII · unique_ptr · shared_ptr · weak_ptr
在 C 里你得 malloc 完记得 free,累不累?C++ 的答案是 RAII(资源获取即初始化):把资源绑在一个对象的生命周期上,对象出生时拿资源,对象销毁时自动还资源。哪怕中间抛异常、提前 return,析构函数也一定会跑。智能指针就是 RAII 的明星产品。
论为什么别再裸 new/delete
裸指针 T* p = new T; 有两个致命问题:① 忘了 delete,内存泄漏;② 中间抛异常或提前 return,delete 那行没走到。把它交给智能指针,这些烦心事自动消失。
三种智能指针
| 智能指针 | 所有权模型 |
|---|---|
std::unique_ptr<T> | 独占:这块内存只有它一个主人。不能拷贝,只能 std::move 移走。首选,零开销。 |
std::shared_ptr<T> | 共享:引用计数,几个人一起用,最后一个走了才释放。有少量开销。 |
std::weak_ptr<T> | 旁观者:不增加引用计数,专门解决 shared_ptr 循环引用导致谁都不释放的问题。 |
现代写法:make_unique / make_shared
#include <memory>
// 独占:离开作用域自动 delete,不用你管
auto p = std::make_unique<int>(42);
std::cout << *p << "\n";
// 共享:两个指针指向同一个 int,引用计数=2
auto a = std::make_shared<int>(100);
auto b = a; // 可以拷贝,引用计数变 2
// a 和 b 都离开作用域时,内存才真正释放
// unique_ptr 不能拷贝,只能移动:
auto p1 = std::make_unique<int>(1);
auto p2 = std::move(p1); // 所有权从 p1 转给 p2,p1 变空
循环引用死锁
两个对象互相持有 shared_ptr 指向对方,引用计数永远降不到 0,谁都不释放,内存泄漏。解法:把其中一方改成 weak_ptr——它"看着"对方但不占引用计数。口诀:能用 unique_ptr 就别用 shared_ptr;用 shared_ptr 时警惕循环,循环了就上 weak_ptr。