Search code examples
c++c++11shared-ptrsmart-pointers

Lifetime of object pointed to by shared pointer


Take the following example

struct A {
   int x = 0;
};

struct B {
   std::shared_ptr<A> mA;
   
   void setA(std::shared_ptr<A> a) {
     mA = a;
   }
};

struct C {

  B initB() {
    A a;
    
    A *aPtr = &a;
    B b;
    b.setA(std::make_shared<A>(aPtr));
    return b;
 } 
};

Now in main() method

C c;
B b = c.initB();

Local variable a in initB() goes out of scope one function finishes executing. However there is a shared pointer pointing to it. Will the object the shared pointer points get deleted when a goes out of scope? Finally will *(b.mA) give a valid instance of A?


Solution

  • Firstly, this doesn't compile.

        B initB() {
            A a;
    
            A* aPtr = &a;
            B b;
            b.setA(std::make_shared<A>(/*aPtr*/a););
            return b;
        }
    

    You have to pass the actual object being made shared, not a pointer to it. Now, to find out the answer to this problem, we can write notifiers for each function, and add constructors and destructors so we can see what is happening.

    #include <memory>
    
    #include <iostream>
    
    struct A {
        int x = 0;
        A() {
            std::cout << "A's CTOR" << std::endl;
        }
        ~A() {
            std::cout << "A's DTOR" << std::endl;
        }
    };
    
    struct B {
    
        B() {
            std::cout << "B's CTOR" << std::endl;
        }
    
        ~B() {
            std::cout << "B's DTOR" << std::endl;
        }
    
        std::shared_ptr<A> mA;
    
        void setA(std::shared_ptr<A> a) {
            std::cout << "Entering setA()" << std::endl;
            mA = a;
            std::cout << "Exiting setA()" << std::endl;
        }
    };
    
    struct C {
    
        C() {
            std::cout << "C's CTOR" << std::endl;
        }
    
        ~C() {
            std::cout << "C's DTOR" << std::endl;
        }
    
        B initB() {
            std::cout << "Entering initB()" << std::endl;
            A a;
    
            A* aPtr = &a;
            B b;
            b.setA(std::make_shared<A>(/*aPtr*/a));
            std::cout << "Exiting initB()" << std::endl;
            return b;
        }
    };
    
    int main() {
        std::cout << "Entering Main" << std::endl;
        C c;
        B b = c.initB();
        std::cout << "Exiting Main" << std::endl;
        return 0;
    }
    

    The output:

    Entering Main
    C's CTOR
    Entering initB()
    A's CTOR
    B's CTOR
    Entering setA()
    Exiting setA()
    Exiting initB()
    B's DTOR
    A's DTOR
    Exiting Main
    B's DTOR
    A's DTOR
    C's DTOR
    

    Interesting, did you catch what happened? There are 2 A's DTORs. std::make_share<A>(a) actually makes a copy of a and then makes a shared_ptr to a. Since we didn't define a copy assignment operator/constructor, the compiler automatically makes one, and that is why we only have one A's CTOR. So even though I can't imagine a place where you would do this, it will have a valid instance of A.