std::shared_ptr循环引用会导致内存泄漏,因引用计数无法归零;应使用std::weak_ptr在非拥有权方向打破循环,访问前必须调用lock()检查有效性。

std::shared_ptr 循环引用会导致内存泄漏
当两个或多个 std::shared_ptr 相互持有对方管理的对象时,引用计数永远无法归零,对象不会被析构——这不是悬空指针问题,而是资源永久驻留。典型场景是父子类双向关联、观察者与被观察者、图结构节点互相引用等。
用 std::weak_ptr 替换循环中的一方持有关系
std::weak_ptr 不增加引用计数,只“观察”所指向对象是否还存活。它不能直接解引用,必须先调用 lock() 转为 std::shared_ptr,失败则说明对象已被释放。
- 只在“非拥有权”方向使用
std::weak_ptr:比如子节点持父节点的std::weak_ptr,而非std::shared_ptr - 访问前必须检查:调用
lock()后判断返回的std::shared_ptr是否为空 - 不能用
weak_ptr.get()或直接解引用 —— 编译不通过,强制你处理生命周期不确定性
一个典型的父子类循环引用修复示例
下面代码中,Parent 持有 Child 的 std::shared_ptr,而 Child 改用 std::weak_ptr 持有 Parent,从而打破循环:
#include#include struct Parent; struct Child; struct Parent { std::shared_ptr child; ~Parent() { std::cout << "Parent destroyed\n"; } }; struct Child { std::weak_ptr parent; // ← 关键:不增加引用计数 ~Child() { std::cout << "Child destroyed\n"; } }; int main() { auto p = std::make_shared (); auto c = std::make_shared (); p->child = c; c->parent = p; // weak_ptr assignment,无引用计数变化 // 安全访问 parent(需检查 lock() 结果) if (auto parent_ptr = c->parent.lock()) { std::cout << "Parent still alive\n"; } else { std::cout << "Parent already gone\n"; } } // ← 此处 p 和 c 都会正确析构
容易忽略的细节和陷阱
很多人以为只要用了 std::weak_ptr 就万事大吉,但实际还有几个关键点:
立即学习“C++免费学习笔记(深入)”;
-
std::weak_ptr本身不保证线程安全:多个线程同时调用lock()是安全的,但后续对所获shared_ptr的使用仍需同步 - 不能用
std::weak_ptr初始化另一个std::shared_ptr(如std::shared_ptr会抛(weak_ptr) std::bad_weak_ptr异常),必须用lock() - 在析构函数中调用
lock()可能返回空 —— 因为此时对象可能正处在销毁链中,其他shared_ptr已释放 - 若存在多重间接引用(A→B→C→A),仅改一处
weak_ptr不够,得识别整个环并至少打断一处
shared_ptr 成员,把“被依赖方”换成 weak_ptr。










