Search code examples
c++shared-ptr

Multiple shared_ptrs to same object, one corrupted


I wrote a program where I make use of shared_ptr to the store the nodes of a graph. Each node has another object associated which keeps a shared_ptr to the node it belongs to. It seems like this is not the proper way to do it, as the child's shared_ptr to the parent is stuck (the use_count() doesn't increase when I create more shared_ptrs of the parent after construction).

ideone demo

The parent:

struct Parent : public enable_shared_from_this<Parent>
{
    Child myChild;

    Parent() : 
        myChild(shared_ptr<Parent>(this)) // can't use getptr() here -> bad_weak_ptr
    {}


    shared_ptr<Parent> getptr()
    {
        return shared_from_this();
    }
};

The child:

struct Child
{
    shared_ptr<Parent> ptr;

    Child(shared_ptr<Parent> a) :
        ptr(a)
    {}
};

Test:

shared_ptr<Parent> p(new Parent);
cout << "UC of parent: " << p.use_count() << endl;
cout << "UC of child ptr: " << p->myChild.ptr.use_count() << endl;

shared_ptr<Parent> p2(p);
cout << "UC of parent: " << p2.use_count() << endl;
cout << "UC of child ptr: " << p2->myChild.ptr.use_count() << endl;

Output:

UC of parent: 1
UC of child ptr: 1
UC of parent: 2
UC of child ptr: 1    // expected 2!

I noticed that everything works as expected when I create the Parent through its normal ctor (no new) and get the shared_ptr via getptr(). Can someone elaborate on this? Am I doing something stupid?

Thanks in advance!


Solution

  • The problem is you created 2 different memory control blocks. One in the child by having it create a shared_pointer to the Parent object, and then by manually creating a shared_pointer using the newed pointer instead of calling getptr() which in turn calls shared_from_this which re-uses the existing control block.

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    struct Parent;
    
    struct Child
    {
        shared_ptr<Parent> ptr;
    
        Child(Parent* a) :
            ptr(a)
        {}
    };
    
    struct Parent : public enable_shared_from_this<Parent>
    {
        Child myChild;
    
        Parent() : 
            myChild(this)
        {}
    
    
        shared_ptr<Parent> getptr()
        {
            return shared_from_this();
        }
    };
    
    int main()
    {
        shared_ptr<Parent> p = (new Parent)->getptr(); // <-- The mistake was not using shared_from_this here, this creating 2 unrelated use counts for the same object
        cout << "UC of parent: " << p.use_count() << endl;
        cout << "UC of child ptr: " << p->myChild.ptr.use_count() << endl;
    
        shared_ptr<Parent> p2(p);
        cout << "UC of parent: " << p2.use_count() << endl;
        cout << "UC of child ptr: " << p2->myChild.ptr.use_count() << endl;
        return 0;
    }
    

    Output on Coliru is:

    g++ -std=c++14 main.cpp && ./a.out
    UC of parent: 2
    UC of child ptr: 2
    UC of parent: 3
    UC of child ptr: 3
    

    Your first clue for this mistake should have been that after: shared_ptr<Parent> p(new Parent); your counts were both 1 and 1, it should have been 2 since you have 2 shared pointers to 1 Parent object.

    P.S: That being said, I'm not sure this is the best design for what you're trying to achieve, but that is a whole other question/debate.