weak_ptr用于打破shared_ptr循环引用,不增加引用计数,需通过lock()转为shared_ptr才能安全访问对象;典型场景包括双向链表、父子节点、观察者模式;使用时须检查有效性,避免竞态与内存泄漏。

weak_ptr 用来打破 shared_ptr 的循环引用
当两个对象通过 shared_ptr 相互持有对方时,引用计数永远不会归零,导致内存泄漏——weak_ptr 就是为此而生的。它不增加引用计数,只“观察”目标对象是否还活着。
典型场景:双向链表节点、树结构中父-子关系、观察者模式里被观察对象持有一组观察者(而观察者又需访问被观察对象)。
关键点:weak_ptr 本身不管理资源,不能直接解引用;必须先调用 lock() 转成 shared_ptr 才能安全访问对象——如果对象已被释放,lock() 返回空 shared_ptr。
如何用 weak_ptr 解决循环引用问题
核心操作就是把其中一端的强引用换成弱引用。比如 A 持有 B 的 shared_ptr,B 就不该再用 shared_ptr 持有 A,而该用 weak_ptr。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 识别出“非拥有关系”的那一方:比如子节点知道父节点是谁,但不负责其生命周期——这里父指针就该是
weak_ptr - 访问前必须检查有效性:
if (auto p = parent.lock()) { /* 安全使用 p */ },不能跳过这步直接解引用 - 避免在构造函数里从
weak_ptr构造shared_ptr并长期保存——这等于又变回了强引用,失去意义 - 注意
weak_ptr自身也有开销(内部含控制块指针),但远小于shared_ptr
weak_ptr.lock() 和 expired() 的区别与选择
expired() 只判断对象是否已销毁,返回 bool;lock() 尝试获取一个有效的 shared_ptr,成功则增加引用计数,失败返回空 shared_ptr。
常见错误:
- 先调
expired()再调lock()—— 这之间可能发生析构,造成竞态(尤其多线程下)。应直接用lock()并检查返回值 - 把
weak_ptr存在容器里却忘了定期清理已失效的项,导致容器持续膨胀。可用erase-remove模式配合expired()清理 -
lock()成功后得到的shared_ptr生命周期仅限当前作用域,别误以为它能延长原对象寿命
和 raw pointer、unique_ptr 混用要注意什么
weak_ptr 只能由 shared_ptr 构造,不能从裸指针或 unique_ptr 创建。混用时容易踩坑:
- 不要用裸指针去“绕过”
weak_ptr的安全性——比如存一个Node*同时又用weak_ptr,一旦裸指针悬空而weak_ptr未检查,行为未定义 -
unique_ptr表示独占所有权,跟weak_ptr的语义冲突。若需“观察”一个unique_ptr管理的对象,只能转为shared_ptr(例如用std::move交出所有权后再建weak_ptr),否则无法构造 - 跨线程传递
weak_ptr是安全的,但lock()的结果是否有效仍取决于目标对象是否在其他线程被释放——没有自动同步机制
真正难处理的从来不是语法,而是谁该拥有对象、谁只是临时借用——weak_ptr 不解决设计模糊,只帮你在设计清晰的前提下守住内存安全。










