Fundamentals 5 min read

When Does Shallow Copy Break? Mastering Deep vs Shallow Copy in C++

This article explains the difference between shallow and deep copying in C++, shows why the default copy constructor can cause double‑deletion when a class holds pointers, and demonstrates how to implement a proper deep‑copy constructor with clear code examples.

Liangxu Linux
Liangxu Linux
Liangxu Linux
When Does Shallow Copy Break? Mastering Deep vs Shallow Copy in C++

C++ provides two kinds of object copying: shallow copy and deep copy. When an object is assigned using the equals sign, the copy constructor is invoked.

Shallow Copy vs. Deep Copy

If no explicit copy constructor is defined, the compiler generates a default one that performs a shallow copy, copying each member bit‑by‑bit. This works when the class contains only plain data members, but if the class holds pointers, both the original and the copy will point to the same memory. When the objects are destroyed, the same heap block is freed twice, leading to undefined behavior such as memory leaks or crashes.

Simple Shallow‑Copy Example

class A {
    public:
        A(int _data) : data(_data) {}
        A() {}
    private:
        int data;
};

int main() {
    A a(5), b = a; // shallow copy, b.data becomes 5
}

In this case the copy is harmless because the class contains only an integer.

Problematic Shallow Copy with Dynamic Memory

class A {
    public:
        A(int _size) : size(_size) {
            data = new int[size];
        }
        A() {}
        ~A() { delete[] data; }
    private:
        int* data;
        int size;
};

int main() {
    A a(5), b = a; // default shallow copy
}

Here the compiler‑generated copy constructor copies the pointer value, so both a and b refer to the same heap block. When b is destroyed it frees the memory, and later a tries to free it again, causing double‑free and undefined behavior.

Implementing a Deep‑Copy Constructor

class A {
    public:
        A(int _size) : size(_size) {
            data = new int[size];
        }
        // Deep‑copy constructor
        A(const A& other) : size(other.size) {
            data = new int[size];
            std::copy(other.data, other.data + size, data);
        }
        ~A() { delete[] data; }
    private:
        int* data;
        int size;
};

int main() {
    A a(5), b = a; // now uses deep copy, no double‑free
}

The custom copy constructor allocates a new buffer and copies the contents, ensuring each object manages its own memory.

In summary, shallow copy simply copies member values and is safe only when a class does not own resources such as heap memory, files, or system handles. When a class owns such resources, a deep copy—allocating separate resources for the copy—is required to avoid dangling pointers and double‑deletion.

memory managementdeep copyC++shallow copyCopy Constructor
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.