一、为什么需要智能指针?
C++ 原生裸指针存在三个致命问题:
忘记 delete → 内存泄漏
重复 delete → 程序崩溃(野指针)
异常跳转导致没执行 delete → 泄漏
智能指针核心思想:用 RAII 机制管理堆内存生命周期。
智能指针本质是 栈对象,出作用域自动析构,自动释放堆内存,彻底解决手动内存管理问题。
二、四种智能指针总览(C++11 标准)
std::unique_ptr:独占所有权(最常用、零开销)
std::shared_ptr:共享所有权(引用计数、有开销)
std::weak_ptr:弱引用,不计数,解决循环引用
std::auto_ptr:C++98 废弃,有坑(拷贝即转移所有权,极其坑,unique_ptr不允许拷贝,只能移动,相当于告知使用者后果)
三、unique_ptr(独占式智能指针)
1. 核心特性
独占所有权:同一时间只能有一个指针管理对象
禁止拷贝(拷贝构造/拷贝赋值 delete)
允许移动(std::move)
无引用计数、开销为 0,等效裸指针
出作用域自动析构释放内存
2. 基本用法
// 创建独占指针
std::unique_ptr<int> ptr(new int(100));
// C++14 推荐:make_unique(更安全、异常安全)
auto ptr = std::make_unique<int>(100);
// 禁止拷贝
// auto ptr2 = ptr; // 编译报错
// 允许移动
auto ptr2 = std::move(ptr);
// 手动释放
ptr2.reset();3. 适用场景
绝大多数单所有者场景(网络连接、上下文对象、文件句柄)
性能要求高、不想有引用计数开销
你的 mempool-cpp、Reactor 连接对象最适合用 unique_ptr
4. 自定义删除器
unique_ptr 支持自定义析构逻辑,适合管理 fd、socket、文件:
auto fdDeleter = [](int* fd) { close(*fd); delete fd; };
std::unique_ptr<int, decltype(fdDeleter)> fdPtr(new int(123), fdDeleter);四、shared_ptr(共享式智能指针)
1. 核心特性
共享所有权,多个指针可以管理同一个堆对象
内部维护 引用计数(原子变量,线程安全)
每多一个 shared_ptr,计数+1;每析构一个,计数-1
计数为 0 自动释放堆内存
2. 基本用法
// 推荐 make_shared(内存一次性分配,效率更高)
auto p1 = std::make_shared<int>(666);
auto p2 = p1; // 计数 +1
std::cout << p1.use_count() << std::endl; // 2
p1.reset(); // 计数 -1
p2.reset(); // 计数 0,释放内存3. 开销问题(面试重点)
需要两块内存:对象内存 + 控制块内存(计数、删除器)
引用计数是 atomic 原子操作,多线程安全,但有性能损耗
高频服务、高性能框架不建议大量用 shared_ptr
4. 经典坑:循环引用
struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };
// 循环引用导致内存永远不释放
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a;两个对象互相持有对方 shared_ptr,引用计数永远不为 0 → 内存泄漏。
解决:其中一端改用 weak_ptr。
五、weak_ptr(弱引用指针)
1. 核心特性
不增加引用计数
不拥有对象所有权,仅“观察”对象
可以检测对象是否已经销毁
只能从 shared_ptr 构造
2. 核心用法
auto sp = std::make_shared<int>(100);
std::weak_ptr<int> wp = sp;
if (!wp.expired()) {
// 提升为 shared_ptr 安全使用
auto p = wp.lock();
}
sp.reset();3. 解决循环引用(最终正确写法)
struct A { std::shared_ptr<B> b; };
struct B { std::weak_ptr<A> a; }; // 弱引用,不计数此时生命周期完全正常释放。
六、三种智能指针选型总结
默认全部用 unique_ptr:性能最高、无开销、最简单、不会循环引用
确实需要多线程/多所有者共享对象 才用 shared_ptr
需要观察、不希望影响生命周期 用 weak_ptr
七、重点分析
1. unique_ptr 和 shared_ptr 区别?
unique_ptr 独占所有权、禁止拷贝、无引用计数、零开销;shared_ptr 共享所有权、带原子引用计数、支持拷贝、有性能开销。优先使用 unique_ptr。
2. make_unique / make_shared 优势?
统一内存分配、异常安全、避免裸指针泄露、代码更简洁。make_shared 一次性分配对象+控制块,内存碎片更少。
3. weak_ptr 作用?
解决 shared_ptr 循环引用问题,不增加引用计数,可以安全判断对象是否存活。
4. shared_ptr 线程安全吗?
引用计数线程安全(原子操作),对象本身读写不安全,多线程修改对象需要自己加锁。