Loading...

Copy Constructor and Assignment Operator in C++

Learn copy constructors and assignment operators in C++ to understand copy semantics, object ownership, and resource management.

In C++, when objects are assigned or copied to one another, two special mechanisms come into play: the Copy Constructor and the Assignment Operator (=). Both transfer data between objects, but they differ in timing and purpose. In this article, we’ll learn the differences, usage examples, and the concept of deep copy.


1. What is a Copy Constructor?

The copy constructor is a special constructor that is called when a new object is created as a copy of an existing one.


class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor called." << endl;
    }

    // Copy Constructor
    Book(const Book &other) {
        title = new string(*other.title); // Deep copy
        cout << "Copy constructor called." << endl;
    }

    ~Book() {
        delete title;
        cout << "Destructor called." << endl;
    }

    void Show() const { cout << "Book: " << *title << endl; }
};

Usage:


int main() {
    Book b1("C++ Programming");
    Book b2 = b1; // Copy constructor is called

    b1.Show();
    b2.Show();
}

Output:


Constructor called.
Copy constructor called.
Book: C++ Programming
Book: C++ Programming
Destructor called.
Destructor called.

In this case, each object has its own separate memory (deep copy). If only the address were copied (shallow copy), both objects would share the same memory, and when the destructor is called, a “double delete” error would occur.


2. What is the Assignment Operator (=)?

The assignment operator is used to assign values from one object to another that has already been created. This means the object already exists; only its contents are replaced.


class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor called." << endl;
    }

    // Assignment Operator (copy assignment operator)
    Book& operator=(const Book &other) {
        cout << "Assignment operator called." << endl;

        // Self-assignment check
        if (this == &other)
            return *this;

        // Delete old memory
        delete title;

        // Create new copy
        title = new string(*other.title);

        return *this;
    }

    ~Book() {
        delete title;
        cout << "Destructor called." << endl;
    }

    void Show() const { cout << "Book: " << *title << endl; }
};

Usage:


int main() {
    Book b1("Modern C++");
    Book b2("Python Guide");

    b2 = b1; // Assignment operator is called
    b1.Show();
    b2.Show();
}

Output:


Constructor called.
Constructor called.
Assignment operator called.
Book: Modern C++
Book: Modern C++
Destructor called.
Destructor called.

Here, both objects have different memory addresses, but their contents are identical. The old data was safely deleted and replaced with the new one.


3. Shallow Copy Issue

If a copy constructor or assignment operator is not defined, the C++ compiler automatically creates a “shallow copy.” In this case, only the address is copied, and the actual data is shared.


class ShallowBook {
public:
    string *title;

    ShallowBook(string t) {
        title = new string(t);
    }

    ~ShallowBook() {
        delete title;
    }
};

int main() {
    ShallowBook b1("C++ Primer");
    ShallowBook b2 = b1; // Automatic shallow copy

    cout << *b1.title << endl;
    cout << *b2.title << endl; // Same memory area!

} // Destructor deletes twice → crash

Therefore, in classes that use dynamic memory, both the copy constructor and assignment operator must be defined manually.


4. Safe Usage with Deep Copy

Deep copying duplicates the actual data into a new memory area. This ensures that each object manages its own memory independently.


class DeepBook {
private:
    string *title;

public:
    DeepBook(string t) {
        title = new string(t);
    }

    // Deep copy constructor
    DeepBook(const DeepBook &other) {
        title = new string(*other.title);
    }

    // Deep copy assignment operator
    DeepBook& operator=(const DeepBook &other) {
        if (this == &other) return *this;
        delete title;
        title = new string(*other.title);
        return *this;
    }

    ~DeepBook() {
        delete title;
    }

    void Show() const { cout << *title << endl; }
};

int main() {
    DeepBook b1("Learning Deep Copy");
    DeepBook b2 = b1; // Copy constructor
    DeepBook b3("Empty");
    b3 = b1; // Assignment operator

    b1.Show();
    b2.Show();
    b3.Show();
}

Each object manages its own memory; the destructor is safely called for each one.


5. The Rule of Three

If a class:

→ Then all three should be defined together. If one is defined and the others are not, the compiler will generate the missing ones automatically, which can lead to unexpected behavior.

Since C++11: the “Rule of Five” concept was introduced, which also includes the move constructor and move assignment operator.


6. Practical Example: Copying and Assigning Books

The following example demonstrates how the copy constructor and assignment operator work together.


#include <iostream>
#include <string>
using namespace std;

class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor: " << *title << endl;
    }

    // Copy constructor
    Book(const Book &other) {
        title = new string(*other.title);
        cout << "Copy constructor: " << *title << endl;
    }

    // Assignment operator
    Book& operator=(const Book &other) {
        cout << "Assignment operator: " << *other.title << endl;
        if (this == &other) return *this;
        delete title;
        title = new string(*other.title);
        return *this;
    }

    ~Book() {
        cout << "Destructor: " << *title << endl;
        delete title;
    }

    void Show() const { cout << *title << endl; }
};

int main() {
    Book b1("C++ Fundamentals");
    Book b2 = b1;      // Copy constructor
    Book b3("Temporary");
    b3 = b1;           // Assignment operator

    cout << "----- Output -----" << endl;
    b1.Show();
    b2.Show();
    b3.Show();
}

Output:


Constructor: C++ Fundamentals
Copy constructor: C++ Fundamentals
Constructor: Temporary
Assignment operator: C++ Fundamentals
----- Output -----
C++ Fundamentals
C++ Fundamentals
C++ Fundamentals
Destructor: C++ Fundamentals
Destructor: C++ Fundamentals
Destructor: C++ Fundamentals

As shown, both object copying and assignment are performed safely and under full control.


7. TL;DR

  • Copy constructor → Creates a new object as a copy of an existing one.
  • Assignment operator → Assigns the contents of one existing object to another.
  • If deep copy is not implemented, “double delete” errors can occur.
  • Rule of Three → Destructor, Copy Constructor, and Assignment Operator should all be defined together.
  • Rule of Five (C++11) → Includes move constructor and move assignment operator.
  • All examples can be safely compiled and run on Visual Studio 2022 or GCC 11+.

Related Articles