Search code examples
c++11shared-ptr

How does the counters work when a class is referenced by another class and with getters


Problem description

I am trying to understand how the shared_ptr counters work. In my class A when I used a getter the counter incremented by 1. When used an instance of A in class B the counter incremented by 2. When I got Done with class B the destructor of A got called twice.

What I didn't understand and what confused me is the following:

  • Why did the counter of A increment by 1 when we called the getter a.a(). Is it because we returned a pointer by Copy so now we have 2 pointers right?

  • Why do we have a counter of 3 when the desctructor of A was called?

  • In class B why did the counter increment by 2?

  • After finishing of class B how was the destructor of A called?

  • When I use reset in the both destructors it gets weirder.


Source Code

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    class A {
        public:
        A(int a):_a( make_shared<int>(a)) {cout <<"A constructor" << endl;}
        ~A(){/*_a.reset();*/cout <<"After destructor, number of As "<< _a.use_count() <<endl;}
        std::shared_ptr<int> a(){return _a;}
        //private:
         std::shared_ptr<int> _a;
    };
    
    class B {
        public:
        B(A a):_b( make_shared<A>(a)) {cout <<"B constructor" << endl;}
        ~B(){ /*_b.reset();*/ cout <<"After destructor, number of Bs "<< _b.use_count() << endl;}
         std::shared_ptr<A> b(){return _b;}
        private:
          std::shared_ptr<A> _b;
    };
    
    
    
    int main()
    {  
        int number = 10;
        A a(number);
        cout <<"counter of A is " << a._a.use_count() << endl;
        cout <<"counter of A is " << a.a().use_count() << endl;
        
        B b(a);
        cout <<"counter of A is " << a.a().use_count() << endl;
        cout <<"counter of B is " << b.b().use_count() << endl;
        
        return 0;
}

  A--->Number: Counter = 1
  B(constructor) pass A by value : counter = 2
  B--->Number: Counter = 3

Output without reset

A constructor
counter of A is 1
counter of A is 2
B constructor
After destructor, number of As 3
counter of A is 3
counter of B is 2
After destructor, number of Bs 1
After destructor, number of As 2
After destructor, number of As 1

Output with reset

A constructor
counter of A is 1
counter of A is 2
B constructor
After destructor, number of As 0
counter of A is 3
counter of B is 2
After destructor, number of As 0
After destructor, number of Bs 0
After destructor, number of As 0


Solution

  • A quick answer

    • Everytime you call a.a() it returns a new shared pointer to the resources. The first one is the member variable. They are eventually released but there's no quarantees as you don't store the returned shared pointer into a variable (which would bind it to a scope).
    • The first After destructor, ... is because you copy value of A a into constructor of B. If you wish to avoid copy, then use a reference B(A& a).
    • The number 3 is because of three shared_pointers, the A a internal one, the returned but not released a.a() result AND the copy of A a passed to the constructor of B.
    • In class B you also return new shared pointer in b.b() -> increment counter by one.

    I hope this covers all of your questions, I can try to clarify further if you have any questions.