shared_ptr构造应优先用make_shared以避免异常安全问题和提升性能;循环引用需用weak_ptr打破;自定义删除器须谨慎处理捕获和异常;get/use_count/unique等函数仅限特定场景使用。

shared_ptr 构造时为什么不能直接用 new
因为 shared_ptr 的构造函数是 explicit 的,且接受的是原始指针,但直接传 new MyClass() 容易引发异常安全问题:如果后续构造中抛出异常,new 分配的内存就泄漏了。
- 正确做法是用
std::make_shared—— 它在一块内存里同时构造控制块和对象,原子完成,无泄漏风险() - 仅当需要自定义删除器,或必须用已存在的裸指针(如 C API 返回)时,才用
shared_ptr,且务必确保不会中途抛异常(new T) -
make_shared还有性能优势:减少一次内存分配(控制块和对象共用一块堆内存)
循环引用怎么破:weak_ptr 不是摆设
两个 shared_ptr 互相持有对方所管理的对象,引用计数永远不为 0,导致内存无法释放——这是最典型的循环引用场景。
- 打破循环的关键不是“少用 shared_ptr”,而是把其中一端改成
weak_ptr,比如父-子关系中,子持有父的weak_ptr -
weak_ptr不增加引用计数,调用lock()才能临时转成shared_ptr;若原对象已销毁,lock()返回空shared_ptr - 注意:
weak_ptr本身不保证线程安全,多个线程同时lock()是安全的,但结果可能不同(一个拿到有效指针,另一个拿到空)
自定义删除器的写法和陷阱
当资源不是普通堆内存(比如 FILE*、pthread_mutex_t、OpenGL handle),必须用自定义删除器,否则默认的 delete 会出错甚至崩溃。
- 删除器可以是函数指针、lambda 或 functor,类型需与
shared_ptr中的D匹配 - lambda 捕获要谨慎:捕获 this 或其他 shared_ptr 可能引入隐式循环引用;推荐值捕获或不捕获
- 删除器对象会被拷贝进
shared_ptr控制块,所以它应该轻量;避免在删除器里做耗时或可能抛异常的操作 - 示例:
shared_ptr(fopen("a.txt", "r"), [](FILE* f) { if (f) fclose(f); })
get() / use_count() / unique() 这些成员函数别乱用
它们看起来简单,但常被误用于“判断资源是否还活着”或“做条件分支”,这违背 RAII 原则,也容易引发竞态。
立即学习“C++免费学习笔记(深入)”;
-
get()返回裸指针——只应在必须传给 C 接口时用;之后绝不能用它 delete 或长期保存,否则破坏所有权语义 -
use_count()是调试用的,非原子读取,多线程下返回值可能瞬间过期;不要用它写 if 分支逻辑 -
unique()等价于use_count() == 1,同样不可靠;想确保独占?用std::move()转移所有权,而不是检查 - 真正该关注的是:对象生命周期是否由 shared_ptr 自然管理?有没有意外延长或提前释放?
智能指针不是语法糖,是资源生命周期契约。用错 make_shared、忽略 weak_ptr 的时机、在多线程里依赖 use_count()——这些都不是小疏忽,而是会埋下静默崩溃或泄漏的根。











