循环引用导致shared_ptr引用计数无法归零,对象永不析构而内存泄漏;因a、b互持对方shared_ptr,main结束时各自use_count仍为1;weak_ptr需lock()并判空使用,且不可长期保存临时shared_ptr。

循环引用直接导致 shared_ptr 的引用计数卡在大于 0 的值,对象永远不析构,内存就一直占着不放。
为什么引用计数归不了零
当 A 持有 B 的 shared_ptr,B 也持有 A 的 shared_ptr,它们的引用计数彼此“互相喂食”:
- A 的引用计数包含:外部变量(比如
main里的node1)+ B 持有的那个shared_ptr - B 的引用计数同理,也包含 A 持有的那个
shared_ptr - 当
main函数结束,node1和node2这两个局部shared_ptr被销毁,各自引用计数减 1 —— 但还剩 1(来自对方) - 于是两者都卡在
use_count() == 1,析构函数不触发,内存泄漏坐实
典型出问题的结构模式
不是所有双向关系都会踩坑,但以下几类结构极易中招:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 父子节点(
Node::parent和Node::child都是shared_ptr<node></node>) - 观察者模式中,被观察者持观察者的
shared_ptr,观察者又持被观察者的shared_ptr - 链表节点的
next和prev都用shared_ptr(尤其环形链表) - 类 A 和类 B 相互持有对方的
shared_ptr成员(如 A 中有shared_ptr<b></b>,B 中有shared_ptr<a></a>)
weak_ptr::lock() 不是万能解药
很多人以为只要用了 weak_ptr 就万事大吉,但关键在怎么用:
-
weak_ptr本身不增加引用计数,但它不能直接解引用 ——wptr->或*wptr编译不过 - 必须先调
lock()得到临时shared_ptr,且要立刻检查是否为空:if (auto p = wptr.lock()) { ... } - 不能把
lock()的返回值长期保存——它只是临时延长生命周期,不是所有权转移 - 构造
weak_ptr必须来自已存在的shared_ptr;不能从裸指针或this直接构造(否则 UB),要用enable_shared_from_this配合shared_from_this()
最容易被忽略的一点:循环引用不会报错、不会崩溃、甚至不抛异常,它安静地吃掉内存,直到程序跑久后变慢或 OOM。上线前不测引用计数、不看析构日志,基本发现不了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










