Home | Projects | Notes > C++ Programming > Shallow Copy vs. Deep Copy
Consider a class that contains a pointer as a data member. Constructor allocates storage dynamically and initializes the pointer. Destructor releases the memory allocated by the constructor. What happens in the default copy constructor?
Member-wise copy of all the member attributes
Each data member is copied from the source object.
The pointer is copied, NOT what it points to (shallow copy)
You end up with a newly created object, and the object being copied both pointing to the same area of storage in the heap.
Problem
The source and the newly created object BOTH point to the SAME data area!
When we release the storage in the destructor, the other object still refers to the released storage!
Creates a copy of the data pointed to by the pointer, NOT just the pointer itself.
This usually means that we have to allocate storage for the data to be copied and then perform the copy.
Each copy will have a pointer to UNIQUE storage in the heap and both areas will contain the same data.
ALWAYS use a copy constructor that does a deep copy when you have a raw pointer as a class data member.
Shallow copy
xxxxxxxxxx301class Shallow2{3private:4 int *data;5public:6 Shallow(int d); // Constructor7 Shallow(const Shallow &source); // Copy constructor (shallow copy)8 ~Shallow(); // Destructor9};10
11// Constructor12Shallow::Shallow(int d)13{14 data = new int; // Dynamically allocate storage15 *data = d;16}17
18// Destructor19Shallow::~Shallow()20{21 delete data; // Free storage22 cout << "Destructor freeing data" << endl;23}24
25// Copy constructor (shallow copy)26Shallow::Shallow(const Shallow &source)27 : data(source.data)28{29 cout << "Copy constructor (shallow)" << endl; 30}The following sample main will likely to crash.
xxxxxxxxxx121int main()2{3 Shallow obj1{100};4 display_shallow(obj1);5 // obj1's data has been released!6 7 obj1.set_data_value(1000);8 Shallow obj2{obj1};9 cout << "Hello world" << endl;10 11 return 0;12}L4:
obj1is passed to this function by value, so a copy ofobj1will get created upon entering the function body. And when the function returns, the copy ofobj1will get destroyed. Sinceobj1and the copy that was just destroyed pointed to the same memory space,obj1now points to invalid storage. If we try to access that storage fromobj1, our program could crash. Also, when the destructor forobj1eventually gets called, it will try to release memory that's no longer valid and will probably crash.
Deep copy
xxxxxxxxxx311class Deep2{3private:4 int *data;5public:6 Deep(int d); // Constructor7 Deep(const Deep &source); // Copy constructor (deep copy);8 ~Deep(); // Destructor9};10
11// Constructor12Deep::Deep(int d)13{14 data = new int; // Dynamically allocate storage15 *data = d;16}17
18// Destructor19Deep::~Deep()20{21 delete data; // Free storage22 cout << "Destructor freeing data" << endl;23}24
25// Copy constructor (deep copy)26Deep::Deep(const Deep &source)27{28 data = new int; // Dynamically allocate storage29 *data = *source.data;30 cout << "Copy constructor (deep)" << endl; 31}xxxxxxxxxx61// Copy constructor (deep copy) - Delegating constructor2Deep::Deep(const Deep &source)3 : Deep{*source.data} // Delegating to Deep(int)4{5 cout << "Copy constructor (deep)" << endl; 6}The following method causes no issue.
xxxxxxxxxx41void display_deep(Deep s)2{3 cout << s.get_data_value() << endl;4}When
sgoes out of scope, the destructor is called and releasesdata. No problem since the storage being released is unique tos
Also, the following sample main will not crash.
xxxxxxxxxx101int main()2{3 Deep obj1{100};4 display_deep(obj1);5 6 obj1.set_data_value(1000);7 Deep obj2{obj1};8 9 return 0;10}