std::weak_ptr通过不参与引用计数、仅提供可验证的临时强引用(lock())来打破shared_ptr循环引用;典型用于双向链表、观察者模式、父子关系等场景。

std::weak_ptr 怎么破掉 shared_ptr 的循环引用
它不参与引用计数,只“观察”对象是否还活着。只要把循环中的一环换成 std::weak_ptr,就能让 std::shared_ptr 正常析构。
典型场景是双向链表节点、观察者模式里的回调持有、父子对象关系(比如 Parent 持有 std::shared_ptr<child></child>,而 Child 又反向持有 Parent)。
- 必须用
lock()转成std::shared_ptr才能安全访问对象,返回空std::shared_ptr表示原对象已销毁 - 不能直接解引用
weak_ptr:写*wp或wp->foo()会编译失败 -
expired()是轻量检查,但仍是竞态敏感的——调用完立刻就可能过期,真正要用还得靠lock()
class Node {
public:
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 这里用 weak,断开循环
};为什么不能用 raw pointer 或 unique_ptr 替代 weak_ptr
裸指针(Node*)不管理生命周期,容易悬空;std::unique_ptr 无法共享所有权,父子关系里子对象不能同时被父和外部持有。
std::weak_ptr 的价值不在“弱”,而在“可验证的临时强引用”——它唯一能做的事就是配合 lock() 拿到一个带引用计数保护的 std::shared_ptr。
立即学习“C++免费学习笔记(深入)”;
- 用裸指针替代
weak_ptr:一旦所指对象被释放,访问必崩溃,且无编译或运行时提示 - 用
std::unique_ptr做反向引用:编译不过,因为unique_ptr不支持拷贝,也无法被多个地方持有 -
weak_ptr构造开销极小(只是拷贝控制块指针),但lock()有原子操作成本,别在热循环里频繁调用
weak_ptr.lock() 返回空 shared_ptr 的常见原因
不是 bug,是设计行为。只要对应对象已被所有 std::shared_ptr 释放,lock() 就返回空 —— 这正是你要的安全边界。
容易误判的情况:对象刚被最后一个 shared_ptr 离开作用域,但析构函数还没执行完(此时 weak_ptr 仍可能 lock() 成功);或者多线程下,另一线程刚好完成了释放。
- 别写
if (wp.expired()) { /* do something */ }然后直接用裸指针——这中间可能已析构 - 正确姿势:始终用
auto sp = wp.lock(); if (sp) { /* use sp */ } - 如果
lock()频繁返回空,说明上游shared_ptr生命周期太短,该检查谁提前释放了它
weak_ptr 和 enable_shared_from_this 一起用时的坑
enable_shared_from_this 提供 shared_from_this(),但它要求对象**本身已被 shared_ptr 管理过**。否则调用直接抛 std::bad_weak_ptr 异常。
常见错误是在构造函数里就调 shared_from_this(),此时对象还没被任何 std::shared_ptr 持有,内部 weak_ptr 还没初始化。
- 永远不要在构造函数、成员初始化列表、或 this 尚未被
std::shared_ptr包裹前调用shared_from_this() -
shared_from_this()底层就是调weak_ptr::lock(),所以同样要检查返回值(虽然它默认 throw,但可配weak_from_this()来避免异常) - 继承
enable_shared_from_this后,别手动 delete this —— 它内部 weak_ptr 会失效,后续 lock 必空
该断的引用得靠 weak_ptr 主动切,而不是等析构顺序碰运气。最易忽略的是:weak_ptr 本身不保活,lock 失败不是异常而是常态,得按“对象可能随时消失”来组织逻辑。











