不能在构造函数里调用 weak_from_this(),因为其内部 weak_ptr 在对象被 shared_ptr 管理后才初始化,构造时调用会抛 std::bad_weak_ptr 异常;必须通过 std::make_shared 创建对象以确保安全。

为什么不能在构造函数里调用 weak_from_this()
因为 std::enable_shared_from_this 的内部弱引用指针(weak_ptr)是在对象被成功构造、且首次通过 std::shared_ptr 管理后才被初始化的。构造函数执行时,对象尚未被任何 shared_ptr 持有,此时调用 weak_from_this() 会抛出 std::bad_weak_ptr 异常。
常见错误现象:terminate called after throwing an instance of 'std::bad_weak_ptr',尤其在构造函数末尾或初始化列表中直接调用时发生。
- 必须确保对象已由
std::make_shared(或std::shared_ptr显式构造)创建,而非new+ 手动包装 -
std::make_shared<t>()</t>是安全前提:它原子性地分配对象和控制块,保证weak_from_this()可用 - 若用
std::shared_ptr<t>(new T)</t>,控制块与对象内存分离,enable_shared_from_this的内部指针可能未正确绑定
weak_from_this() 和 shared_from_this() 的分工逻辑
weak_from_this() 不提升引用计数,只返回一个观察者视角的 std::weak_ptr;而 shared_from_this() 会尝试加锁并返回一个强引用 —— 这正是它危险的地方:如果对象已被销毁,shared_from_this() 会抛异常;而 weak_from_this() 总是安全调用(只要对象本身还“活着”)。
典型使用场景:异步回调、延迟任务、观察者注册等需要避免循环引用又不确定对象生命周期是否延续的场合。
立即学习“C++免费学习笔记(深入)”;
- 先用
weak_from_this()获取weak_ptr,再在真正需要时调用lock()尝试升级为shared_ptr -
lock()返回空shared_ptr表示对象已析构,可安全跳过后续操作 - 不要在 lambda 捕获中直接写
[self = shared_from_this()]() { ... },这会延长生命周期、可能造成悬挂或循环引用
多继承下 enable_shared_from_this 的陷阱
如果类从多个基类继承,且其中不止一个继承了 std::enable_shared_from_this,编译器无法确定该调用哪个基类的 weak_from_this(),导致二义性错误。
错误信息示例:error: request for member 'weak_from_this' is ambiguous
- 只让最派生类(即最终被
make_shared创建的那个类)继承std::enable_shared_from_this - 若必须多层参与,用虚继承 + 显式作用域限定,例如
Base1::weak_from_this(),但更推荐重构为单点继承 - 注意:即使基类模板参数不同(如
enable_shared_from_this<a></a>和enable_shared_from_this<b></b>),仍可能因 ADL 或模板推导引发冲突
线程安全边界:weak_from_this() 本身是线程安全的,但 lock() 不是银弹
weak_from_this() 是 const 成员函数,不修改内部状态,因此多线程并发调用是安全的;但 weak_ptr::lock() 虽然线程安全,其返回结果仅代表“调用瞬间”的对象存活状态 —— 下一行代码执行时对象仍可能被销毁。
这意味着:仅靠 if (auto ptr = weak_from_this().lock()) { ... } 并不能完全规避竞态,特别是当 ... 中涉及复杂逻辑或耗时操作时。
- 对关键资源访问,应在
lock()后立即完成最小必要操作,避免长时间持有强引用 - 若需跨多步操作保障对象存活,应设计成“检查 → 处理 → 再检查”,或引入外部同步机制(如
std::shared_mutex) - 注意:
weak_ptr的lock()不阻塞,也不提供内存序保证,不适用于替代锁
真正容易被忽略的是:哪怕你全程只用 weak_from_this() 和 lock(),只要对象的析构函数里做了非平凡操作(比如触发回调、释放共享资源),就可能和正在 lock() 的线程形成时序依赖 —— 这时候问题已不在语法层面,而在对象生命周期契约的设计上。


















