What is the cyclic dependency issue with shared_ptr?

后端 未结 2 1300
有刺的猬
有刺的猬 2020-12-19 03:05

I read about shared pointers and understood how to use. But I never understood the cyclic dependency problem with shared pointers and how weak pointers are going to fix thos

相关标签:
2条回答
  • 2020-12-19 03:39
    shard_ptr<A> <----| shared_ptr<B> <------
        ^             |          ^          |
        |             |          |          |
        |             |          |          |
        |             |          |          |
        |             |          |          |
    class A           |     class B         |
        |             |          |          |
        |             ------------          |
        |                                   |
        -------------------------------------
    

    Now if we make the shared_ptr of the class B and A, the use_count of the both pointer is two.

    When the shared_ptr goes out od scope the count still remains 1 and hence the A and B object does not gets deleted.

    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;  
    }
    

    output:

    A()
    B()
    

    As we can see from the output that A and B pointer are never deleted and hence memory leak.

    To avoid such issue just use weak_ptr in class A instead of shared_ptr which makes more sense.

    0 讨论(0)
  • 2020-12-19 03:41

    The problem isn't that complex. Let --> represent a shared pointer:

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

    So we've got ourselves a circular dependency with shared pointers. What's the reference count of each object?

    A:  2
    B:  1
    C:  1
    

    Now suppose the rest of the program (or at any rate the part of it that holds a shared pointer to A) is destroyed. Then the refcount of A is reduced by 1, so the reference count of each object in the cycle is 1. So what gets deleted? Nothing. But what do we want to be deleted? Everything, because none of our objects can be reached from the rest of the program any more.

    So the fix in this case is to change the link from C to A into a weak pointer. A weak pointer doesn't affect the reference count of its target, which means that when the rest of the program releases A, its refcount hits 0. So it's deleted, hence so is B, hence so is C.

    Before the rest of the program releases A, though, C can access A whenever it likes by locking the weak pointer. This promotes it to a shared pointer (and increases the refcount of A to 2) for as long as C is actively doing stuff with A. That means if A is otherwise released while this is going on then its refcount only falls to 1. The code in C that uses A doesn't crash, and A is deleted whenever that short-term shared pointer is destroyed. Which is at the end of the block of code that locked the weak pointer.

    In general, deciding where to put the weak pointers might be complex. You need some kind of asymmetry among the objects in the cycle in order to choose the place to break it. In this case we know that A is the object referred to by the rest of the program, so we know that the place to break the cycle is whatever points to A.

    0 讨论(0)
提交回复
热议问题