C++中unique_ptr如何避免循环依赖问题?
我们知道shared_ptr存在循环依赖问题:比如对象A有一个指向对象B的shared_ptr成员,对象B也有一个指向对象A的shared_ptr成员,那么这两个对象会一直驻留在内存直到程序结束——因为它们的引用计数永远无法降到0。
那如果把示例中的shared_ptr换成unique_ptr,为什么不会出现这种情况?此时A仍指向B,B也指向A,看起来两者都无法被销毁才对?
编辑:根据要求补充示例代码:
#include <iostream> #include <memory> using namespace std; class B; class A{ public: unique_ptr<B> a_ptr; }; class B{ public: unique_ptr<A> b_ptr; }; int main() { A* a = new A(); B* b = new B(); a->a_ptr.reset(b); b->b_ptr.reset(a); return 0; }
首先要明确:你给出的示例代码同样会导致内存泄漏,两个对象都无法被正常销毁,只是原因和shared_ptr的循环依赖不一样。
shared_ptr的循环依赖是引用计数机制导致的:shared_ptr是共享所有权模型,每多一个指向对象的shared_ptr,引用计数就加1;当shared_ptr被销毁或重置,引用计数减1。只有当引用计数降到0时,对象才会被销毁。双向循环持有shared_ptr时,每个对象的引用计数至少为1(对方的shared_ptr还持有自己),外部引用释放后计数也无法归0,所以对象永远不会被销毁。unique_ptr的情况是析构的循环死锁:unique_ptr是独占所有权模型,同一时刻只能有一个unique_ptr拥有对象的所有权。你的示例中,A的unique_ptr<B>持有B的所有权,B的unique_ptr<A>持有A的所有权。
要销毁A,必须先销毁它的成员a_ptr(也就是销毁B);但销毁B时,又必须先销毁它的成员b_ptr(也就是销毁A)——这就形成了析构的死循环,两个对象的析构函数都无法被触发,最终内存泄漏。
之所以会有“unique_ptr不会出现循环依赖问题”的错觉,是因为unique_ptr的独占设计从根源上就不鼓励这种双向持有的场景:正常使用中,unique_ptr代表唯一的所有者,你不会刻意让两个对象互相成为对方的所有者,这种写法本身就违背了它的设计意图。而shared_ptr的共享模式,更容易在不经意间(比如双向链表节点)形成循环依赖。
内容的提问来源于stack exchange,提问作者sadcat_1

