Overview
In C++, copying objects can lead to serious bugs if you're dealing with raw pointers. By default, C++ uses shallow copy, which means only the pointer's value is copied—not the data it points to.
This tutorial covers:
- What shallow vs deep copy means
- The problems caused by shallow copy
- How to implement deep copy correctly
- A practical class example with dynamic memory
- When to use Rule of ThreevsRule of Five
What Is Shallow Copy?
A shallow copy copies the values of member variables as-is. If your class has a pointer member, both the original and copy point to the same memory.
class Shallow {
public:
    int* data;
    Shallow(int val) {
        data = new int(val);
    }
    ~Shallow() {
        delete data;
    }
};Now consider:
Shallow a(10);
Shallow b = a;  // default copy constructorThis causes both a.data and b.data to point to the same memory. When both destructors run, delete is called twice on the same pointer — undefined behavior!
What Is Deep Copy?
A deep copy duplicates the actual data pointed to, not just the pointer.
class Deep {
public:
    int* data;
    Deep(int val) {
        data = new int(val);
    }
    // Copy constructor for deep copy
    Deep(const Deep& other) {
        data = new int(*other.data);
    }
    // Assignment operator for deep copy
    Deep& operator=(const Deep& other) {
        if (this != &other) {
            delete data;
            data = new int(*other.data);
        }
        return *this;
    }
    ~Deep() {
        delete data;
    }
};Rule of Three
If your class handles dynamic memory:
- Copy Constructor
- Copy Assignment Operator
- Destructor
You must implement all three. This is called the Rule of Three.
Example: Deep Copy for a String Wrapper
class String {
private:
    char* buffer;
public:
    String(const char* str) {
        buffer = new char[strlen(str) + 1];
        strcpy(buffer, str);
    }
    // Copy constructor
    String(const String& other) {
        buffer = new char[strlen(other.buffer) + 1];
        strcpy(buffer, other.buffer);
    }
    // Assignment operator
    String& operator=(const String& other) {
        if (this != &other) {
            delete[] buffer;
            buffer = new char[strlen(other.buffer) + 1];
            strcpy(buffer, other.buffer);
        }
        return *this;
    }
    ~String() {
        delete[] buffer;
    }
    void print() const {
        std::cout << buffer << std::endl;
    }
};Usage
String a("Hello");
String b = a;       // deep copy
String c("World");
c = a;              // deep assignmentAll objects manage their own memory independently.
Modern C++: Rule of Five
In C++11 and newer, also consider:
- Move Constructor
- Move Assignment Operator
This is the Rule of Five. Add move semantics if your class is performance-sensitive and uses resource ownership.
Conclusion
When your class uses raw pointers:
- Avoid shallow copies.
- Always implement deep copy logic.
- Follow the Rule of Three (or Rule of Five).
- Prefer std::string,std::vector, or smart pointers in modern C++.
Understanding deep copy is essential for writing robust, bug-free C++ code.
Discussion 0
Please sign in to join the discussion.
No comments yet. Start the discussion!