weak_ptr不能直接访问对象,因其设计初衷是不参与所有权管理,故不提供operator->或operator*;必须先调用lock()获取shared_ptr,且仅当对象存活时返回非空指针。

weak_ptr 为什么不能直接访问对象
weak_ptr 的设计初衷就是「不参与所有权管理」,所以它不提供 operator-> 或 operator*。直接写 wp->foo() 会编译失败——这不是 bug,是强制你显式处理对象可能已销毁的风险。
正确做法是先调用 lock():它返回一个 shared_ptr,且仅当原对象还活着时才非空。一旦 lock() 返回空 shared_ptr,说明目标已被释放。
- 别用
expired()判断后再lock()——这有竞态风险,两次调用间对象可能被析构 - 别把
lock()结果存为shared_ptr后长期持有,除非你真想延长对象生命周期 - 如果只是读状态(比如检查是否还在运行),
lock()+ 空指针检查足够,不用额外加锁
循环引用破除:weak_ptr 在观察者模式中的典型用法
当 A 持有 shared_ptr<b></b>,B 又持有 shared_ptr<a></a>,两者 refcount 永远不为 0,内存泄漏。解决方法是让其中一方改用 weak_ptr。
例如观察者模式中,被观察者(Subject)通常持有观察者(Observer)的 shared_ptr(保证观察者存活期间能收到通知);而观察者内部若需回调 Subject,就该用 weak_ptr<subject></subject>,避免反向强引用:
class Observer {
weak_ptr<subject> subject_;
public:
void onEvent() {
auto sp = subject_.lock(); // 尝试获取强引用
if (sp) sp->handleResponse(); // 对象还在,安全调用
// 否则跳过,不崩溃
}
};</subject>
- Subject 析构时,所有指向它的
weak_ptr自动失效,lock()返回空 - Observer 不负责 Subject 生命周期,符合「观察者不拥有被观察者」的语义
- 注意:不能在
Subject的析构函数里调用Observer方法,此时weak_ptr::lock()必定为空
weak_ptr::lock() 和 expired() 的性能与语义差异
expired() 是轻量级检查,只读取控制块里的 weak count;lock() 除了检查,还会原子递增 shared count——这意味着它有轻微开销,但换来的是线程安全的对象访问权。
- 纯判断存在性(如日志、调试),用
expired()足够 - 只要后续要访问对象成员,必须用
lock(),否则多线程下可能刚判完未过期,下一毫秒就被析构 - 频繁调用
lock()并立即释放(比如轮询)可能影响性能,考虑用shared_ptr缓存或重构逻辑
构造 weak_ptr 的唯一合法方式
weak_ptr 无法直接绑定裸指针或 new 出来的对象——它必须从已有 shared_ptr 构造,或通过 shared_ptr 的拷贝/赋值间接获得。
- 错误:
weak_ptr<t> wp(new T);</t>→ 编译失败 - 正确:
auto sp = make_shared<t>(); weak_ptr<t> wp(sp);</t></t> - 正确:
weak_ptr<t> wp = sp;</t>(sp是shared_ptr<t></t>) - 注意:
shared_ptr析构后,所有由它构造的weak_ptr都变为expired()
真正容易被忽略的是:weak_ptr 的生命周期独立于其所观察的对象,但它所依赖的控制块(control block)必须存在才能调用 lock()。一旦最后一个 shared_ptr 和最后一个 weak_ptr 都销毁,控制块才释放——这点决定了 weak_ptr 本身不引发资源泄漏,但也不能替代 careful ownership design。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











