shared_ptr的循环依赖问题是什么?

时间:2014-03-05 00:08:31

标签: c++ shared-ptr weak-ptr

我阅读了有关共享指针的内容并了解了如何使用。但我从来没有理解共享指针的循环依赖问题以及弱指针如何解决这些问题。任何人都可以清楚地解释这个问题吗?

2 个答案:

答案 0 :(得分:10)

问题并不复杂。让-->表示共享指针:

The rest of the program  --> object A --> object B
                                    ^     |
                                     \    |
                                      \   v
                                        object C

所以我们用共享指针获得了循环依赖。每个对象的引用计数是多少?

A:  2
B:  1
C:  1

现在假设程序的其余部分(或者无论如何是它的一部分,它包含一个指向A的共享指针)被破坏。然后A的refcount减少1,因此循环中每个对象的引用计数为1.那么什么被删除?没有。但是我们想要删除什么?一切,因为我们的任何对象都不能从程序的其余部分到达。

因此,在这种情况下的修复是将链接从C更改为A到弱指针。弱指针不影响其目标的引用计数,这意味着当程序的其余部分释放A时,其引用计数达到0.因此它被删除,因此B也是如此,因此C也是如此。

在程序的其余部分发布A之前,C可以通过锁定弱指针随时访问A.只要C正在主动执行A的操作,这就会将它提升为共享指针(并将A的引用次数增加到2)。这意味着如果在此过程中A被释放,则其引用计数仅降至1。 C中使用A的代码不会崩溃,只要该短期共享指针被销毁,就会删除A.这是锁定弱指针的代码块的末尾。

一般来说,决定将弱指针放在哪里可能很复杂。您需要在循环中的对象之间存在某种不对称性,以便选择打破它的位置。在这种情况下,我们知道A是程序其余部分所引用的对象,因此我们知道打破循环的地方是A的任何点。

答案 1 :(得分:3)

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

现在,如果我们创建类B和A的shared_ptr,则两个指针的use_count为2。

shared_ptr超出范围时,计数仍保持为1,因此不会删除A和B对象。

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

输出:

A()
B()

从输出中可以看到,A和B指针永远不会被删除,因此会导致内存泄漏。

为避免此类问题,只需在类A中使用weak_ptr而不是shared_ptr即可。