Home > Mobile >  How does a shared_ptr handle copy to a pure virtual base class?
How does a shared_ptr handle copy to a pure virtual base class?

Time:08-20

Class B expects to receive an instance of shared_ptr<IError>.
Class A implements IError and is passed by value to the constructor of B.

I would like to understand how this scenario is handled. How does the shared_ptr as a template class handle the conversion to IError?

In a simple case where B receives shared_ptr<A> I assume the copy constructor is called and the reference counter is increased. However since IError is pure virtual a normal copy constructor invocation seems not to be case here?

// Example program

#include <iostream>
#include <string>

class IError 
{    
    public:
        virtual ~IError(){};
        virtual void one rror() = 0;
};

 

class A : public IError 
{
    public:
       A(){};
       void one rror(){std::cout << "Error Occured" << std::endl;} 
};

class B
{
  public:
     B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){}
     void Action(){mErrorReporter->OnError();}

  private:   
     std::shared_ptr<IError> mErrorReporter;
};

int main()
{
    auto objA = std::make_shared<A>();
    auto objB = std::make_shared<B>(objA);
    objB->Action();
}

CodePudding user response:

Debugging time! Let's find out what happens by using the tools we have available as developers.

The memory of the shared_ptr objA looks like this (type &objA in the memory window; it will be replaced by its address):

objA

It has a pointer to the object (000002172badd8e0) and a pointer to the control block.

The control block looks like this (copy and paste the second value into a new memory window):

objA control block

It has a pointer to the allocator (first 2 columns), the reference count (1) and the weak reference count (0 offset 1).

After objB has been created, the control block of objA has changed to a reference count of 2:

objA control block after copy

And the shared_ptr objB looks like this:

objB

It points to the a shared pointer and to the control block.

Shared pointer of objB

The shared pointer in objB points to the same object as before (000002172badd8e0), so no copy of the actual object has been made.

The control block of objB indicates that objB only has a reference count of 1:

objB control block

a normal copy constructor invocation seems not to be case here?

Correct. No copy of the object is made, as we can confirm with a debugger. But a copy of the shared_ptr has been made.

CodePudding user response:

It doesn't.


Copy a shared_ptr doesn't copy it's point-to object, just like normal pointer

std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<IError> i = a;
A* a = new A;
IError* i = a; // no copy A

CodePudding user response:

You are right in that the base class IError is abstract, hence it cannot be instantiated, never mind copied.

The code below has been modified from the original to show how each newly created shared_ptr just increments the reference count of the original shared_ptr. So, a shallow copy.

In your code, as well as in the code below, the underlying object to these shared_ptrs is concrete class A, derived from the abstract IError, so it is legal to shallow copy it.

// Example program

#include <iostream>
#include <string>
#include <memory>

class IError 
{    
    public:
        virtual ~IError(){};
        virtual void one rror() = 0;
};

class A : public IError 
{
    public:
       A(){std::cout << "A created.\n";};
       void one rror(){std::cout << "Error Occured" << std::endl;} 
};

class B
{
  public:
     B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){
         std::cout << "B created from A.\n";
     }
     void Action(){mErrorReporter->OnError();}

  private:   
     std::shared_ptr<IError> mErrorReporter;
};

int main()
{
    auto objA = std::make_shared<A>();
    std::cout << "I. Reference count for objA: " << objA.use_count() << '\n'; 
    auto objB = std::make_shared<B>(objA);
    std::cout << "II. Reference count for objA: " << objA.use_count() << '\n'; 
    // objB->Action();
    auto objBB = std::make_shared<B>(*objB);
    std::cout << "Created objBB from objB\n"; 
    std::cout << "III. Reference count for objA: " << objA.use_count() << '\n';    
    std::cout << "Reference count for objB: " << objB.use_count() << '\n'; 
    std::cout << "Reference count for objBB: " << objBB.use_count() << '\n';
    // auto objB_from_base = std::make_shared<B>(IError()); // ERROR: IError is an abstract class
}

with output:

A created.
I. Reference count for objA: 1
B created from A.
II. Reference count for objA: 2
Created objBB from objB
III. Reference count for objA: 3
Reference count for objB: 1
Reference count for objBB: 1
  • Related