std::shared_ptr因循环引用可能导致内存泄漏,当对象间相互持有shared_ptr时引用计数无法归零;解决方法是使用std::weak_ptr打破循环,weak_ptr不增加引用计数,通过lock()临时获取shared_ptr以安全访问对象,从而避免泄漏。

在C++中,std::shared_ptr 是一种常用的智能指针,通过引用计数自动管理动态内存。每个 shared_ptr 持有对象的引用计数,当最后一个 shared_ptr 被销毁时,所管理的对象才会被释放。但这种机制有一个关键缺陷:容易导致循环引用,从而引发内存泄漏。
什么是循环引用?
当两个或多个对象通过 std::shared_ptr 相互持有对方时,它们的引用计数永远无法降为0,即使外部已不再使用这些对象。结果是:对象不会被析构,造成内存泄漏。
例如:
struct node; using NodePtr = std::shared_ptr<Node>; struct Node { NodePtr parent; NodePtr child; }; auto parent = std::make_shared<Node>(); auto child = std::make_shared<Node>(); parent->child = child; child->parent = parent; // 循环引用形成
此时,parent 和 child 的引用计数都是2。离开作用域后,各自的引用计数减1,变为1,但由于仍存在相互引用,对象不会被释放。
立即学习“C++免费学习笔记(深入)”;
如何解决循环引用问题?
关键是打破强引用链条。C++ 提供了 std::weak_ptr 来解决这个问题。weak_ptr 不增加引用计数,只观察 shared_ptr 所管理的对象,在需要时可临时升级为 shared_ptr。
修改上面的例子:
 struct Node {     std::weak_ptr<Node> parent; // 使用 weak_ptr     NodePtr child; }; 
这样,child 持有 parent 的弱引用,不会增加其引用计数。当 parent 离开作用域,引用计数正确归零,对象被释放,child 同样如此。
使用 weak_ptr 的注意事项
weak_ptr 本身不能直接访问对象,必须通过 lock() 获取一个临时的 shared_ptr:
 std::weak_ptr<Node> wp = ...; if (auto sp = wp.lock()) {     // sp 是有效的 shared_ptr,可以安全使用     sp->do_something(); } else {     // 对象已被释放 } 
这确保了线程安全和对象生命周期的正确判断。
基本上就这些。只要在可能形成闭环的地方用 weak_ptr 替代 shared_ptr,就能有效避免循环引用导致的内存泄漏。


