weak_ptr 是不增加引用计数的智能指针,用于打破 shared_ptr 循环引用;它需通过 lock() 转为 shared_ptr 才能安全访问对象,且只能从有效 shared_ptr 构造。

weak_ptr 是什么,为什么能破循环引用
循环引用的本质是两个 shared_ptr 互相持有对方的强引用计数,导致 refcount 永远不为 0,对象无法析构。weak_ptr 不增加 refcount,只“观察”对象是否还活着,因此把它用在“非拥有方”的一端,就能打破强引用闭环。
关键点:只有 weak_ptr 本身不延长对象生命周期;它必须通过 lock() 转成 shared_ptr 才能安全访问对象——这步会检查对象是否已被销毁。
典型场景:父子类之间双向指针
比如 Parent 持有 Child 的 shared_ptr,而 Child 又需要访问 Parent。若 Child 也用 shared_ptr 存 Parent,就构成循环引用。
正确做法是让 Child 用 weak_ptr 存 Parent:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
class Parent;
class Child {
public:
std::weak_ptr<Parent> parent_; // 不增加 Parent 的 refcount
void doSomething() {
auto p = parent_.lock(); // 尝试升级为 shared_ptr
if (p) { // 对象还活着
p->doWork();
} else {
// Parent 已析构,不能访问
}
}
};
lock() 和 expired() 的选择与陷阱
lock() 返回 shared_ptr,内部做原子检查 + refcount 增加,适合后续多次访问;expired() 只检查是否失效,开销更小,但之后仍需 lock() 才能用。
- 频繁访问时,优先用
lock()一次获取有效shared_ptr,避免重复检查 - 仅做存在性判断(如日志、跳过逻辑),用
expired()更轻量 - 绝不能直接解引用
weak_ptr(*wp或wp->)——编译不过,这是常见误写 -
lock()返回空shared_ptr时,解引用会 crash,必须判空
注意构造 weak_ptr 的时机和来源
weak_ptr 只能从 shared_ptr 构造(包括拷贝、赋值),不能直接 new 或从裸指针创建。这意味着:你必须确保原始对象由 shared_ptr 管理,且该 shared_ptr 在构造 weak_ptr 时仍有效。
常见错误:
- 从临时
shared_ptr构造:std::weak_ptr<t>(std::make_shared<t>())</t></t>—— 临时对象立即销毁,weak_ptr从一开始就是expired() - 把
weak_ptr存在比其源shared_ptr生命周期更长的地方,却忘了它可能随时变空 - 跨线程传递
weak_ptr后未同步检查有效性,导致竞态访问已析构对象
真正安全的做法:源 shared_ptr 必须长期存活(例如作为类成员或全局资源),且所有 weak_ptr 使用前都调用 lock() 并检查返回值。

















