c++如何使用智能指针_c++ shared_ptr用法解析【进阶】

8次阅读

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

c++如何使用智能指针_c++ shared_ptr用法解析【进阶】

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()——这些都不是小疏忽,而是会埋下静默崩溃或泄漏的根。

text=ZqhQzanResources