Take a simple class:
1 2 3
|
struct Example {
int* ptr;
};
|
Now, lets play:
1 2 3 4 5 6
|
Example one;
int answer {42};
one.ptr = &answer;
Example two;
// bitwise copy memory of one to memory of two
|
The memory of an Example contains just one pointer.
The memory of one contains address of answer.
After bitwise copy the memory of two contains same bits, the address of answer.
1 2 3 4
|
// copy construct:
Example three { one };
// assert: *(one.ptr) == *(three.ptr) == 42
// assert: one.ptr == three.ptr
|
Default copy constructor does copy each member, like if we had written:
1 2 3
|
Example::Example( const Example& rhs )
: ptr( rhs.ptr )
{}
|
The issue here is that copy construction of simple type, like pointer
ptr
is in practice a bitwise copy.
Hence, for class Example, the default copy constructor is no different from bitwise copy.
All instances, one, two, three point to same integer. The copies are shallow.
If the Example should own the memory that it points to, then a copy should not point to same memory. The copy ctor could be:
1 2 3 4 5
|
Example::Example( const Example& rhs )
: ptr( new int )
{
*ptr = *rhs.ptr; // deep copy of member data
}
|
Take another class:
1 2 3
|
struct Sample {
std::vector<int> vec;
};
|
Now, lets play:
1 2 3 4 5 6
|
Sample uno;
uno.vec.push_back( 42 );
Example duo( duo );
// assert: uno.vec[0] == duo.vec[0] == 42
// assert: &(uno.vec[0]) != &(duo.vec[0])
|
The default copy constructor that makes a shallow copy of a class that has raw pointers
does a proper deep copy of a class that has "sugar-coated pointer", aka vector.