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):
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):
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:
And the shared_ptr objB
looks like this:
It points to the a shared pointer and to the control block.
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:
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_ptr
s 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