In programming, copying objects is a common operation, but it's essential to understand the differences between shallow copy and deep copy to avoid unintended side effects, especially when dealing with complex data structures containing pointers.
What is a Shallow Copy?
A shallow copy of an object duplicates only the object's immediate members. If the object contains pointers or references to other objects, the shallow copy will copy the pointers or references, but not the objects they point to. This means both the original and the copied object will share the same referenced objects.
Example in C:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x;
int y;
int* ptr;
} Point;
int main() {
Point p1;
p1.x = 1;
p1.y = 2;
p1.ptr = (int*)malloc(sizeof(int));
*(p1.ptr) = 10;
// Shallow copy
Point p2 = p1;
printf("p2.x = %d, p2.y = %d, *p2.ptr = %d\n", p2.x, p2.y, *(p2.ptr));
// Modifying p1.ptr will also affect p2.ptr
*(p1.ptr) = 20;
printf("After modifying p1.ptr, *p2.ptr = %d\n", *(p2.ptr));
free(p1.ptr); // Free memory for both p1 and p2 since they share the same pointer
return 0;
}
Output:
p2.x = 1, p2.y = 2, *p2.ptr = 10
After modifying p1.ptr, *p2.ptr = 20
The memory layout look like this:
p1: +------+------+------+
| x | y | ptr |
+------+------+------+
| 1 | 2 | *---|----> 10 (in heap memory)
+------+------+------+
p2: +------+------+------+
| x | y | ptr |
+------+------+------+
| 1 | 2 | *---|----> 10 (same heap memory as p1)
+------+------+------+
Here, both p1
and p2
point to the same memory location for ptr
. Modifying the value pointed by p1.ptr
will also affect p2.ptr
because they share the same memory.
Example in C++:
#include <iostream>
class Point {
public:
int x, y;
int* ptr;
Point(int xVal, int yVal, int ptrVal) : x(xVal), y(yVal) {
ptr = new int(ptrVal);
}
// Shallow copy constructor
Point(const Point& p) : x(p.x), y(p.y), ptr(p.ptr) {}
void display() const {
std::cout << "x = " << x << ", y = " << y << ", *ptr = " << *ptr << std::endl;
}
~Point() {
delete ptr;
}
};
int main() {
Point p1(1, 2, 10);
Point p2 = p1; // Shallow copy
p1.display();
p2.display();
*p1.ptr = 20;
p1.display();
p2.display(); // p2.ptr is also affected
return 0;
}
Output:
x = 1, y = 2, *ptr = 10
x = 1, y = 2, *ptr = 10
x = 1, y = 2, *ptr = 20
x = 1, y = 2, *ptr = 20
What is a Deep Copy?
A deep copy duplicates not only the object itself but also all objects referenced by the original object. This means the copied object and the original object do not share any referenced objects; they are completely independent of each other.
Example in C:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x;
int y;
int* ptr;
} Point;
void deepCopyPoint(Point* dest, const Point* src) {
dest->x = src->x;
dest->y = src->y;
dest->ptr = (int*)malloc(sizeof(int));
*(dest->ptr) = *(src->ptr);
}
int main() {
Point p1;
p1.x = 1;
p1.y = 2;
p1.ptr = (int*)malloc(sizeof(int));
*(p1.ptr) = 10;
Point p2;
// Deep copy
deepCopyPoint(&p2, &p1);
printf("p2.x = %d, p2.y = %d, *p2.ptr = %d\n", p2.x, p2.y, *(p2.ptr));
// Modifying p1.ptr will not affect p2.ptr
*(p1.ptr) = 20;
printf("After modifying p1.ptr, *p2.ptr = %d\n", *(p2.ptr));
free(p1.ptr);
free(p2.ptr);
return 0;
}
Output:
p2.x = 1, p2.y = 2, *p2.ptr = 10
After modifying p1.ptr, *p2.ptr = 10
The memory layout now look like this:
p1: +------+------+------+
| x | y | ptr |
+------+------+------+
| 1 | 2 | *---|----> 10 (in heap memory)
+------+------+------+
p2: +------+------+------+
| x | y | ptr |
+------+------+------+
| 1 | 2 | *---|----> 10 (newly allocated
+------+------+------+ in heap memory)
Example in C++:
#include <iostream>
class Point {
public:
int x, y;
int* ptr;
Point(int xVal, int yVal, int ptrVal) : x(xVal), y(yVal) {
ptr = new int(ptrVal);
}
// Deep copy constructor
Point(const Point& p) : x(p.x), y(p.y) {
ptr = new int(*p.ptr);
}
// Overloaded assignment operator for deep copy
Point& operator=(const Point& p) {
if (this != &p) { // Self-assignment check
x = p.x;
y = p.y;
delete ptr;
ptr = new int(*p.ptr);
}
return *this;
}
void display() const {
std::cout << "x = " << x << ", y = " << y << ", *ptr = " << *ptr << std::endl;
}
~Point() {
delete ptr;
}
};
int main() {
Point p1(1, 2, 10);
Point p2 = p1; // Deep copy using copy constructor
p1.display();
p2.display();
*p1.ptr = 20;
p1.display();
p2.display(); // p2.ptr is not affected
return 0;
}
Output:
x = 1, y = 2, *ptr = 10
x = 1, y = 2, *ptr = 10
x = 1, y = 2, *ptr = 20
x = 1, y = 2, *ptr = 10