10

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

4

2 回答 2

20

问题没那么复杂。让我们-->表示一个共享指针:

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 的任何内容。

于 2014-03-05T00:38:11.193 回答
9
shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

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

当 shared_ptr 超出 od 范围时,计数仍保持为 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 更有意义。

于 2018-08-12T08:03:34.090 回答