weak_ptr是不增加引用计数的智能指针,用于打破shared_ptr循环引用;它仅观察对象,需通过lock()获取临时shared_ptr来安全访问资源。

weak_ptr 是什么,为什么能破循环引用
在 C++ 中,shared_ptr 通过引用计数管理资源生命周期,但两个 shared_ptr 相互持有(比如 A 持有 B 的 shared_ptr,B 也持有 A 的 shared_ptr),会导致引用计数永远不为 0,资源无法释放——这就是循环引用。weak_ptr 不增加引用计数,只“观察”一个由 shared_ptr 管理的对象,因此可用来打破这种僵局。
典型场景:父子类双向关联时怎么用 weak_ptr
常见于树节点、观察者模式、容器与元素互相持有等结构。关键原则是:**一方用 shared_ptr 拥有对方,另一方用 weak_ptr 观察对方**,避免双向强引用。
- 父节点通常“拥有”子节点 → 子节点用
shared_ptr存在父节点的vector中> - 子节点需要访问父节点但不该延长其生命周期 → 子节点中存
weak_ptr而非shared_ptr - 使用前必须调用
lock()获取临时shared_ptr,检查是否仍有效
struct Node {
std::shared_ptr parent;
std::vector> children;
// ✅ 正确:用 weak_ptr 避免反向强引用
std::weak_ptr back_to_parent;
void set_parent(std::shared_ptrzuojiankuohaophpcnNodeyoujiankuohaophpcn p) {
parent = p;
// ✅ 安全赋值:weak_ptr 不影响引用计数
back_to_parent = p;
}
void use_parent() {
// ✅ 必须 lock() 并检查,防止访问已销毁对象
if (auto p = back_to_parent.lock()) {
std::cout zuojiankuohaophpcnzuojiankuohaophpcn "Parent still alive\n";
} else {
std::cout zuojiankuohaophpcnzuojiankuohaophpcn "Parent gone\n";
}
}};
weak_ptr.lock() 和 expired() 的区别与误用风险
lock() 返回 shared_ptr,内部做原子检查+增计数;expired() 只检查引用计数是否为 0,不加锁也不增计数。二者不是等价的判断方式。
立即学习“C++免费学习笔记(深入)”;
- ❌ 错误写法:
if (!ptr.expired()) { auto s = ptr.lock(); /* 使用 s */ } —— 中间可能发生析构,lock() 返回空
- ✅ 正确写法:直接
if (auto s = ptr.lock()) { /* 使用 s */ },一次原子操作完成检查和获取
-
expired() 适合仅需“快照式”判断(如日志、调试),不用于后续访问资源的分支逻辑
注意:weak_ptr 不能直接构造,也不能指向裸指针
weak_ptr 只能从 shared_ptr 构造(拷贝或赋值),且必须与该 shared_ptr 共享同一控制块。试图用裸指针或 unique_ptr 初始化会编译失败。
- ❌
std::weak_ptr wp(new int(42)); —— 编译错误
- ❌
std::weak_ptr wp(std::move(up)); (up 是 unique_ptr)—— 无对应构造函数
- ✅
std::shared_ptr sp = std::make_shared(42); std::weak_ptr wp = sp;
- ⚠️ 即使
sp 被 move 走,只要还有其他 shared_ptr 存活,wp.lock() 仍可能成功
真正容易被忽略的是:weak_ptr 的存在本身不阻止对象销毁,但它依赖的控制块(control block)会在最后一个 shared_ptr 和最后一个 weak_ptr 都析构后才释放。如果大量长期存在的 weak_ptr 不清理,会拖慢控制块回收——尤其在高频创建/销毁对象的场景中,得留意 weak_ptr 的生命周期管理。










