C++ and shallow copy vs. deep copy
In C++, a shallow copy of an object is a copy of the object that shares the same memory for its member data as the original object. In other words, both the original object and its shallow copy refer to the same underlying data. On the other hand, a deep copy of an object is a copy of the object that has its own, separate memory for its member data. In other words, the original object and its deep copy do not share any data.
The difference between shallow and deep copying is important when working with objects that own dynamically allocated memory. When you create a shallow copy of such an object, the copy will share the same memory as the original, meaning that both objects will refer to the same data. This can lead to unexpected behaviour, especially if one of the objects is later modified.
In contrast, when you create a deep copy of an object, the copy will have its own memory, meaning that both objects are completely separate and independent. This is generally the desired behaviour when working with objects that own dynamically allocated memory, as it ensures that each object has its own, isolated data.
Shallow Copy Example
Here is an example to illustrate the shallow copying in C++:
#include <iostream>
using namespace std;
class Box {
public:
int* length;
int height;
void set_dimensions(int l, int h) {
length = new int(l);
height = h;
}
void show_data(const char* str) {
cout << str << '\n'
<< " Length = " << *length << " Addr: " << length
<< "\n Height = " << height << " Addr: " << &height
<< endl << endl;
}
};
int main() {
Box B1;
B1.set_dimensions(14, 16);
B1.show_data("Set Dim B1");
// When copying the data of object at the time of initialization
// then copy is made through COPY CONSTRUCTOR
Box B2 = B1;
B2.show_data("B2 = B1 copy constructor");
// When copying the data of object after initialization then the
// copy is done through DEFAULT ASSIGNMENT OPERATOR
Box B3;
B3 = B1;
B3.show_data("B3 = B1 default assignment operator");
*B1.length = 2;
B1.height = 3;
B1.show_data("b1 - first change");
B2.show_data("b2 - first change");
B3.show_data("b3 - first change");
*B2.length = 4;
B2.height = 5;
B1.show_data("b1 - second change");
B2.show_data("b2 - second change");
B3.show_data("b3 - second change");
*B3.length = 6;
B3.height = 7;
B1.show_data("b2 - third change");
B2.show_data("b3 - third change");
B3.show_data("b4 - third change");
return 0;
}
Below there is some part of the program output, that briefly describes what is shallow copy.
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Set Dim B1
Length = 14 Addr: 0x7e0c20
Height = 16 Addr: 0x7ffecec88658
B2 = B1 copy constructor
Length = 14 Addr: 0x7e0c20
Height = 16 Addr: 0x7ffecec88668
B3 = B1 default assignment operator
Length = 14 Addr: 0x7e0c20
Height = 16 Addr: 0x7ffecec88678
b1 - first change
Length = 2 Addr: 0x7e0c20
Height = 3 Addr: 0x7ffecec88658
b2 - first change
Length = 2 Addr: 0x7e0c20
Height = 16 Addr: 0x7ffecec88668
b3 - first change
Length = 2 Addr: 0x7e0c20
Height = 16 Addr: 0x7ffecec88678
After the dimensions setup for B1
object there is B1.length
initialized on the heap, while B1.height
on the stack. We can see that after initializing B2
with copy constructor B2.length
contains the same address as B1.length = 0x7e0c20
. The same is happening after default assignment operator with B3
, where B3.length
points to the same data as well. height
member is allocated on the stack, that means there is no copy of the address pointing to the data, its just the value.
This will create ambiguity and run-time errors, dangling pointer. Those kind of bugs are difficult to debug.
Now first question would be, how to resolve this issue? In Deep copy, an object is created by copying data of all variables, and it also allocates similar memory resources with the same value to the object. In order to perform Deep copy, we need to explicitly define the copy constructor along with default assignment operator and assign dynamic memory as well, if required. Also, it is required to dynamically allocate memory to the variables in the other constructors, as well.
Deep Copy (resolving issue with Shallow Copy)
Below is an example to illustrate how shallow copy can be resolved. There is added copy constructor along with default assignment operator, which allocates new member on the heap. From now on Box.length
member will have always its own address.
class Box {
public:
int* length;
int height;
Box() :
length(nullptr),
height(0)
{ }
// copy constructor
Box(const Box& b) :
length(new int),
height(b.height)
{
*length = *b.length;
}
// default assignment operator
Box& operator=(const Box& b) {
length = new int;
*length = *b.length;
height = b.height;
return *this;
}
~Box() {
delete length;
}
void set_dimensions(int l, int h) {
length = new int(l);
height = h;
}
void show_data(const char* str) {
cout << str << '\n'
<< " Length = " << *length << " Addr: " << length
<< "\n Height = " << height << " Addr: " << &height
<< endl << endl;
}
};
And there is part of the program output from coliru:
Set Dim B1
Length = 14 Addr: 0x8dac20
Height = 16 Addr: 0x7ffdeab37348
B2 = B1 copy constructor
Length = 14 Addr: 0x8dbc50
Height = 16 Addr: 0x7ffdeab37358
B3 = B1 default assignment operator
Length = 14 Addr: 0x8dbc70
Height = 16 Addr: 0x7ffdeab37368
b1 - first change
Length = 2 Addr: 0x8dac20
Height = 3 Addr: 0x7ffdeab37348
b2 - first change
Length = 14 Addr: 0x8dbc50
Height = 16 Addr: 0x7ffdeab37358
b3 - first change
Length = 14 Addr: 0x8dbc70
Height = 16 Addr: 0x7ffdeab37368
We can briefly see that all members of B1
, B2
and B3
have its own address.
Summary
Shallow Copy | Deep copy |
When we create a copy of object by copying data of all member variables as it is, then it is called shallow copy | When we create an object by copying data of another object along with the values of memory resources that reside outside the object, then it is called a deep copy |
A shallow copy of an object copies all of the member field values. | Deep copy is performed by implementing our own copy constructor and default assignment operator. |
In shallow copy, the two objects are not independent | It copies all fields, and makes copies of dynamically allocated memory pointed to by the fields |
It also creates a copy of the dynamically allocated objects. | If we do not create the deep copy in a rightful way then the copy will point to the original, with disastrous consequences. |
Reference:
https://www.geeksforgeeks.org/shallow-copy-and-deep-copy-in-c/
Other C++ articles:
2 komentarze do “C++ and shallow copy vs. deep copy”
Możliwość komentowania została wyłączona.