shared_ptr循环引用的本质是双方互相持有导致引用计数无法归零,典型场景包括双向链表、父子对象和观察者模式;weak_ptr通过不增加引用计数并配合lock()安全访问来打破循环。

shared_ptr循环引用的本质和典型场景
当两个对象各自用shared_ptr持有对方时,引用计数永远无法归零,导致内存泄漏。最常见于双向链表节点、父子对象(如树结构中父指针和子指针互相持有时)、观察者模式中观察者与被观察者互相强引用。
weak_ptr是打破循环的关键工具
weak_ptr不增加引用计数,只“观察”所指向对象是否还存在。它不能直接访问对象,必须通过lock()升级为shared_ptr——成功则说明对象仍存活,失败(返回空)则说明已被释放。
- 把“非拥有关系”的那一端改为weak_ptr,例如:子节点存父节点用weak_ptr,父节点存子节点用shared_ptr
- 访问前务必调用lock()并检查返回值,避免解引用已释放对象
- weak_ptr本身线程安全(拷贝/赋值/lock不需额外同步),但其指向的对象生命周期仍需逻辑保证
实际写法示例(父子关系)
假设Parent持有children的shared_ptr,Child需反向访问Parent但不延长其生命周期:
struct Parent;
struct Child {
std::weak_ptr parent; // 不参与所有权
void doSomething() {
auto p = parent.lock(); // 尝试获取临时shared_ptr
if (p) {
p->doWork(); // 安全调用
} else {
// Parent已被销毁,做清理或跳过
}
}
};
struct Parent {
std::vector> children;
void addChild() {
auto c = std::make_shared();
c->parent = shared_from_this(); // weak_ptr自动绑定当前shared_ptr
children.push_back(c);
}
};
其他辅助手段与注意事项
除了weak_ptr,还可结合设计层面规避:
立即学习“C++免费学习笔记(深入)”;
- 用原始指针(raw pointer)替代weak_ptr,前提是能严格保证生命周期——父对象一定比子对象活得久
- 显式断开引用:在析构或重置前手动将shared_ptr置为空,尤其在容器或回调中
- 避免在lambda捕获中隐式产生循环:[self = shared_from_this()]会延长自身生命周期,若self又持有该lambda,则成环;此时应捕获weak_ptr并在lambda内lock
weak_ptr不是万能胶,核心在于厘清谁是真正的所有者,让非所有者“弱观察”。用对了,循环引用就自然解开。











