DeveloperBreeze

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 Three vs Rule 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 constructor

This 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 assignment

All 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.

Continue Reading

Handpicked posts just for you — based on your current read.

Discussion 0

Please sign in to join the discussion.

No comments yet. Start the discussion!