std::weak_ptr通过不参与引用计数来打破shared_ptr循环引用,需用lock()转为shared_ptr并判空后访问,不可直接解引用或重复lock,是唯一线程安全、可感知对象生死的非拥有式智能指针。

std::weak_ptr 怎么破掉 shared_ptr 的循环引用?
它不参与引用计数,只“观察”对象是否还活着。只要把循环中的一环换成 std::weak_ptr,就能让 shared_ptr 的引用计数自然归零,对象被释放。
典型场景是双向链表节点、父子对象(比如树节点)、观察者回调里存了 this 的 shared_ptr —— 这些地方一不留神就锁死内存。
- 必须用
lock()把weak_ptr临时转成shared_ptr才能安全访问对象,否则可能解引用已销毁的对象 - 不能直接用
operator->或operator*,weak_ptr没有这些重载 -
expired()只是快速判断,但判断完到真正使用之间仍有竞争窗口,所以推荐直接lock()后判空
为什么不能用 raw pointer 或 unique_ptr 替代 weak_ptr?
裸指针(raw pointer)不管理生命周期,容易悬空;unique_ptr 无法共享所有权,父子关系里父持有子没问题,但子反向持有父会直接编译失败(移动语义冲突)。
weak_ptr 是唯一合法的“非拥有式、线程安全、可感知对象生死”的智能指针类型。
立即学习“C++免费学习笔记(深入)”;
-
weak_ptr构造只能来自同源的shared_ptr或另一个weak_ptr,不能从裸指针构造 - 跨线程传递
weak_ptr是安全的;但lock()返回的shared_ptr生命周期仅限当前作用域,别试图长期缓存它来“延长”对象生命 - 频繁调用
lock()+ 解引用,性能略低于直接用shared_ptr,但这是为打破循环必须付出的代价
常见错误:weak_ptr.lock() 后没检查就用
这是最常踩的坑——lock() 返回的是 shared_ptr,但它可能为空(原对象已被释放),直接解引用会崩溃或未定义行为。
auto sp = wp.lock();
if (sp) {
sp->do_something(); // ✅ 安全
} else {
// ❌ 对象已销毁,别碰它
}
- 别写
if (wp.lock()) { wp.lock()->xxx; }—— 两次lock()可能返回不同结果,第二次可能为空 - 别在 lambda 捕获里直接存
weak_ptr然后隐式调用lock(),比如[wp]{ wp.lock()->f(); },一样有空指针风险 - 调试时注意:
wp.expired()返回 true 不代表“刚销毁”,只代表“当前时刻没有活跃的shared_ptr”,可能是暂时没人持有了
和 enable_shared_from_this 一起用要注意什么?
当类需要把自己作为 shared_ptr 传出去(比如注册回调),得继承 enable_shared_from_this<t></t>,再用 shared_from_this()。但它和 weak_ptr 配合时,容易误以为“只要用了 weak_ptr 就不会循环”——其实不是。
- 如果父类用
shared_ptr持有子类,子类又通过shared_from_this()把自己传给父类的某个成员函数并存储,仍可能形成循环 - 正确做法是:子类对外提供
weak_from_this()(C++17 起),让接收方自己lock(),而不是直接给shared_ptr - 继承
enable_shared_from_this的对象,必须先由make_shared或shared_ptr构造,否则shared_from_this()会抛std::bad_weak_ptr
weak_ptr 来避免循环,往往说明对象图的关系模型本身值得重新梳理。










