Menu
Coddy logo textTech

Copy Constructor

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 20 of 104.

A copy constructor creates a new object as a copy of an existing object. It's called when you initialize an object from another object of the same type, pass an object by value, or return an object by value.

The copy constructor takes a const reference to an object of the same class:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Copy constructor
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Copy constructor called
Player another(original);  // Also calls copy constructor

If you don't define a copy constructor, the compiler generates one that performs a shallow copy - it copies each member's value directly. This works fine for simple types, but causes problems when your class manages dynamic memory.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Deep copy constructor
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Allocate new memory
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copy contents
    }
    
    ~Buffer() { delete[] data; }
};

Without a custom copy constructor, both objects would point to the same memory. When one is destroyed, the other would have a dangling pointer. A deep copy allocates new memory and copies the actual data, ensuring each object owns its own resources.

challenge icon

Challenge

Easy

Let's build a message history system that demonstrates the importance of deep copying when your class manages dynamic memory. You'll create a MessageLog class that stores messages in a dynamically allocated array, and implement a proper copy constructor to ensure each copy has its own independent memory.

You'll create two files to organize your code:

  • MessageLog.h: Define a MessageLog class that manages a collection of messages using dynamic memory. Your class should have:
    • Private members: a pointer to a string array for storing messages, a capacity for the maximum number of messages, and a count for the current number of messages
    • A parameterized constructor that takes a capacity, allocates the array on the heap, and initializes count to 0
    • A copy constructor that performs a deep copy — allocating new memory and copying all messages from the source object
    • A destructor that frees the allocated memory and prints "MessageLog destroyed"
    • An addMessage(string msg) method that adds a message if there's room
    • A getCount() method that returns the current message count
    • A getMessage(int index) method that returns the message at the given index
  • main.cpp: Demonstrate that your copy constructor creates an independent copy. Read a capacity value and two messages from input, then:
    • Create a MessageLog called original with the input capacity
    • Add both messages to original
    • Create a copy called backup using the copy constructor
    • Add a third message "New message" to original only
    • Print "Original count: <count>"
    • Print "Backup count: <count>"
    • Print all messages from backup, one per line

The input format will be:

  • First line: capacity (integer)
  • Second line: first message (string)
  • Third line: second message (string)

If your deep copy is implemented correctly, the backup should have 2 messages while the original has 3 — proving they have independent memory. Without a proper copy constructor, both would share the same array and changes to one would affect the other!

Include your header file in main.cpp using #include "MessageLog.h".

Cheat sheet

A copy constructor creates a new object as a copy of an existing object. It takes a const reference to an object of the same class:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Copy constructor
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Copy constructor called
Player another(original);  // Also calls copy constructor

If you don't define a copy constructor, the compiler generates one that performs a shallow copy - copying each member's value directly. This causes problems when your class manages dynamic memory.

A deep copy constructor allocates new memory and copies the actual data, ensuring each object owns its own resources:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Deep copy constructor
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Allocate new memory
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copy contents
    }
    
    ~Buffer() { delete[] data; }
};

Without a custom copy constructor, both objects would point to the same memory, causing dangling pointers when one is destroyed.

Try it yourself

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Read input
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Create a MessageLog called 'original' with the input capacity

    // TODO: Add both messages to original

    // TODO: Create a copy called 'backup' using the copy constructor

    // TODO: Add "New message" to original only

    // TODO: Print "Original count: <count>"

    // TODO: Print "Backup count: <count>"

    // TODO: Print all messages from backup, one per line

    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming