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
xxxxxxxxxx
301class Shallow
2{
3private:
4 int *data;
5public:
6 Shallow(int d); // Constructor
7 Shallow(const Shallow &source); // Copy constructor (shallow copy)
8 ~Shallow(); // Destructor
9};
10
11// Constructor
12Shallow::Shallow(int d)
13{
14 data = new int; // Dynamically allocate storage
15 *data = d;
16}
17
18// Destructor
19Shallow::~Shallow()
20{
21 delete data; // Free storage
22 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.
xxxxxxxxxx
121int 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:
obj1
is passed to this function by value, so a copy ofobj1
will get created upon entering the function body. And when the function returns, the copy ofobj1
will get destroyed. Sinceobj1
and the copy that was just destroyed pointed to the same memory space,obj1
now points to invalid storage. If we try to access that storage fromobj1
, our program could crash. Also, when the destructor forobj1
eventually gets called, it will try to release memory that's no longer valid and will probably crash.
Deep copy
xxxxxxxxxx
311class Deep
2{
3private:
4 int *data;
5public:
6 Deep(int d); // Constructor
7 Deep(const Deep &source); // Copy constructor (deep copy);
8 ~Deep(); // Destructor
9};
10
11// Constructor
12Deep::Deep(int d)
13{
14 data = new int; // Dynamically allocate storage
15 *data = d;
16}
17
18// Destructor
19Deep::~Deep()
20{
21 delete data; // Free storage
22 cout << "Destructor freeing data" << endl;
23}
24
25// Copy constructor (deep copy)
26Deep::Deep(const Deep &source)
27{
28 data = new int; // Dynamically allocate storage
29 *data = *source.data;
30 cout << "Copy constructor (deep)" << endl;
31}
xxxxxxxxxx
61// Copy constructor (deep copy) - Delegating constructor
2Deep::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.
xxxxxxxxxx
41void display_deep(Deep s)
2{
3 cout << s.get_data_value() << endl;
4}
When
s
goes 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.
xxxxxxxxxx
101int 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}