shared_ptr循环引用导致对象无法析构,因引用计数永不归零;weak_ptr用于非拥有关系,需lock()获取临时shared_ptr安全访问,避免崩溃。

shared_ptr引用计数卡在1以上,对象根本不会析构
因为shared_ptr的释放条件是「引用计数归零」,而互相持有会让双方的计数永远无法降到0。比如A持有shared_ptr<b></b>,B又持有shared_ptr<a></a>,那么即使外部所有shared_ptr都离开作用域,A和B仍各自被对方持有一个强引用——计数至少为1,析构函数压根不会被调用。
典型场景:父子节点、双向链表、观察者与被观察者
这些结构天然需要双向访问,但若全用shared_ptr建模,就极易踩坑:
- 父子关系中,父节点
child用shared_ptr合理(拥有子),但子节点parent若也用shared_ptr,就构成循环 - 双向链表的
next和prev如果都是shared_ptr,两端互相拖住,谁也走不了 - 观察者模式里,
Subject存vector<shared_ptr>></shared_ptr>没问题,但Observer反向持有shared_ptr<subject></subject>就危险
weak_ptr不是“替代品”,而是“非拥有关系”的明确表达
weak_ptr不参与引用计数,只用于观察或临时访问。它不能直接解引用,必须调用lock()获得一个shared_ptr才能安全使用:
std::weak_ptr<node> parent;
// ...
if (auto p = parent.lock()) {
// p 是有效的 shared_ptr,可放心使用
p->do_something();
} else {
// parent 所指对象已销毁
}</node>
关键点:
- 把“被动”“回指”“非生命周期控制方”的那一端换成
weak_ptr,比如子节点的parent、链表节点的prev、观察者的subject -
lock()返回shared_ptr是临时的,仅保证访问期间对象存活;不用时立刻丢弃,不延长生命周期 - 不能把
weak_ptr当成“更轻量的shared_ptr”来滥用——它没有所有权语义,强行绕过lock()直接用会崩溃
容易忽略的细节:make_shared构造时控制块和对象内存是连续分配的
这本身不是循环引用的成因,但会影响调试和检测:
- 用
make_shared创建对象时,控制块(含引用计数)和对象实例在一块内存里;而用shared_ptr<t>(new T)</t>是分开分配的 - 这意味着,即使发生循环引用,你也看不到“对象析构了但控制块还在”这种中间态——两者都一直占着,直到程序退出
- 所以泄漏表现非常静默:没报错、没崩溃、只是
~Node()永远不打印,内存占用缓慢上涨
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











