weak_ptr::lock() 是唯一安全访问对象的方式,它原子地尝试升级为 shared_ptr:成功表示对象存活可安全使用,失败则不可访问;expired() 因存在竞态窗口不可用于资源访问判断。

weak_ptr::lock() 是唯一安全的检查方式
直接访问 weak_ptr 指向的对象是未定义行为,它本身不提供 get() 或解引用操作。唯一可信赖的方法是调用 lock() —— 它尝试升级为 shared_ptr,成功说明对象仍存活,失败(返回空 shared_ptr)说明已被释放。
常见错误是误用 expired():它只反映引用计数是否为 0,但无法排除对象刚被析构、内存尚未回收的竞态窗口;而 lock() 是原子操作,能真正验证“此刻能否安全访问”。
-
expired()返回true⇒ 对象肯定已释放(可快速否定) -
expired()返回false⇒ 不代表对象还活着,只是引用计数 > 0,可能正处在析构中 -
lock()返回非空shared_ptr⇒ 对象存活且可安全使用 -
lock()返回空shared_ptr⇒ 对象已释放或正在析构,不可访问
不要用 expired() 替代 lock() 做资源访问判断
写成 if (!wp.expired()) { auto p = wp.lock(); ... } 是典型误用:两次调用之间存在竞态,expired() 返回 false 后,对象可能在 lock() 执行前就被析构,导致 lock() 返回空,后续解引用崩溃。
正确做法是直接依赖 lock() 的返回值:
auto sp = wp.lock();
if (sp) {
// 安全使用 *sp 或 sp.get()
} else {
// 对象已释放,跳过逻辑或触发清理
}
这个模式天然规避了竞态,且语义清晰——你真正需要的不是“是否过期”,而是“现在能不能用”。
lock() 的性能开销很小,但别在热循环里滥用
lock() 是原子读+引用计数递增,现代标准库实现通常只需几条 CPU 指令,在绝大多数场景下可忽略其开销。但它仍涉及原子操作和潜在的缓存行竞争。
- 单次检查(如事件回调入口)完全没问题
- 高频循环中反复调用(如每帧几十万次)可能成为瓶颈,应考虑缓存
shared_ptr或重构生命周期 - 若确定对象生命周期长于当前作用域,可提前
lock()并持有shared_ptr,避免重复检查
注意自管理对象(如 this)场景下的陷阱
当类内部用 shared_from_this() 创建 weak_ptr 指向自身时,检查逻辑不变,但必须确保对象没被外部提前销毁。常见坑是:
- 构造函数中就保存
weak_ptr<this></this>,但此时shared_ptr尚未建立,lock()必然失败 - 对象被
shared_ptr管理,但外部所有shared_ptr已释放,只剩weak_ptr→lock()失败 - 类继承自
enable_shared_from_this,但首次创建对象时未用make_shared,导致控制块未正确初始化,shared_from_this()抛异常
这类场景下,lock() 结果更关键——它不撒谎,但前提是你的对象确实由 shared_ptr 正确托管。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











