使用weak_ptr打破循环引用,避免内存泄漏。当多个shared_ptr相互持有导致引用计数无法归零时,应将非拥有关系的指针改为weak_ptr,如父子结构中子节点用weak_ptr回连父节点,从而正确释放资源。

在C++中,std::shared_ptr通过引用计数自动管理对象生命周期,但在多个
shared_ptr相互持有对方时,容易发生循环引用,导致内存无法释放。这不仅浪费资源,还可能引发内存泄漏。
循环引用的产生原因
当两个或多个对象通过
shared_ptr互相强引用时,各自的引用计数永远不会归零,析构函数不会被调用。 例如:
struct Node; using NodePtr = std::shared_ptr; struct Node { NodePtr parent; NodePtr child; ~Node() { std::cout << "Node destroyed\n"; } }; auto parent = std::make_shared (); auto child = std::make_shared (); parent->child = child; child->parent = parent;
此时,
parent和
child的引用计数都为2,离开作用域后仍无法释放——形成循环引用。
使用weak\_ptr打破循环
解决方法是将其中一个引用改为std::weak_ptr,它不增加引用计数,仅观察对象是否存在。
立即学习“C++免费学习笔记(深入)”;
修改上面的例子:
struct Node {
NodePtr child; // 强引用
std::weak_ptr parent; // 弱引用
~Node() { std::cout << "Node destroyed\n"; }
};
访问
parent时需通过
lock()检查对象是否存活:
if (auto p = parent.lock()) {
// 安全使用 p
}
这样,当外部指针释放后,引用链会被正确断开,对象得以析构。
检测潜在的循环引用
C++标准库本身不提供自动检测机制,但可通过以下方式辅助排查:
- 使用静态分析工具(如Clang Static Analyzer、Cppcheck)识别长期持有
shared_ptr
的成员变量。 - 在调试版本中添加引用计数日志,观察对象生命周期结束时引用计数是否归零。
- 借助Valgrind、ASan等内存检测工具发现泄漏点,反向追踪引用关系。
设计建议与最佳实践
避免循环引用的根本在于合理设计对象关系:
- 父子结构中,父节点用
shared_ptr
管理子节点,子节点用weak_ptr
回连父节点。 - 观察者模式中,观察者持有被观察者的
weak_ptr
,防止双向强引用。 - 避免在lambda中直接捕获
shared_ptr
自身,必要时使用weak_ptr
捕获来避免延长生命周期。
基本上就这些。关键是理解所有权关系,用
weak_ptr处理非拥有型引用,就能有效规避循环问题。










