CodeNebula
C++98corebeginner

Copy Constructor

C++98 · The Foundation · Compiler flag: -std=c++98

Explanation

A special constructor that creates a new object as a copy of an existing object of the same type.

Real-World Scenario

Duplicate a game save state so the player can experiment without losing progress.

Quick Snippet

snippet.cpp
class Widget { public: Widget(const Widget& other) {} };

Full Example

copy_constructor.cpp
// CodeNebula - C++98 Example: Copy Constructor
// Compile: g++ -std=c++98 -Wall -Wextra -o example copy_constructor.cpp
//
// This example demonstrates:
// - Default (shallow) copy vs custom (deep) copy constructors
// - Why deep copy is essential when a class manages heap memory
// - How the copy constructor is invoked (direct init, pass-by-value, return)

#include <iostream>
#include <cstring>

// ============================================================
// ShallowArray - uses the compiler-generated copy constructor
// (dangerous when the class owns heap memory)
// ============================================================
class ShallowArray {
public:
    int* data;
    int  size;

    ShallowArray(int n) : data(new int[n]), size(n) {
        for (int i = 0; i < size; ++i) {
            data[i] = i * 10;
        }
        std::cout << "  ShallowArray constructed, data @ " << data << std::endl;
    }

    // No custom copy constructor -- compiler generates a member-wise
    // (shallow) copy.  Both objects will point to the SAME memory!

    ~ShallowArray() {
        std::cout << "  ShallowArray destructor, freeing data @ " << data << std::endl;
        delete[] data;
    }

    void print() const {
        std::cout << "  [";
        for (int i = 0; i < size; ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << data[i];
        }
        std::cout << "]" << std::endl;
    }
};

// ============================================================
// DeepArray - provides a proper deep-copying copy constructor
// ============================================================
class DeepArray {
private:
    int* data;
    int  size;

public:
    // Regular constructor
    DeepArray(int n) : data(new int[n]), size(n) {
        for (int i = 0; i < size; ++i) {
            data[i] = i * 10;
        }
        std::cout << "  DeepArray constructed, data @ " << data << std::endl;
    }

    // ---- Custom copy constructor (deep copy) ----
    DeepArray(const DeepArray& other) : data(new int[other.size]), size(other.size) {
        // Allocate fresh memory and copy element-by-element
        for (int i = 0; i < size; ++i) {
            data[i] = other.data[i];
        }
        std::cout << "  DeepArray COPY constructed, new data @ " << data
                  << " (source was @ " << other.data << ")" << std::endl;
    }

    ~DeepArray() {
        std::cout << "  DeepArray destructor, freeing data @ " << data << std::endl;
        delete[] data;
    }

    void set(int index, int value) {
        if (index >= 0 && index < size) {
            data[index] = value;
        }
    }

    void print() const {
        std::cout << "  [";
        for (int i = 0; i < size; ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << data[i];
        }
        std::cout << "]" << std::endl;
    }
};

// Helper: receives a DeepArray by value (triggers copy constructor)
void passByValue(DeepArray arr) {
    std::cout << "  Inside passByValue, array contains:";
    arr.print();
}

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "1. SHALLOW COPY PROBLEM (dangerous!)" << std::endl;
    std::cout << "========================================" << std::endl;
    std::cout << "Creating original ShallowArray of size 3:" << std::endl;

    // We intentionally limit scope so we can see the double-free issue
    // described in comments, but avoid actual undefined behaviour in
    // this demo by only constructing ONE ShallowArray.
    {
        ShallowArray original(3);
        original.print();
        std::cout << "\n  NOTE: If we copied 'original' with the default copy" << std::endl;
        std::cout << "  constructor, BOTH objects would share the same pointer." << std::endl;
        std::cout << "  When they destruct, the memory is freed TWICE -> undefined behaviour!" << std::endl;
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "2. DEEP COPY SOLUTION" << std::endl;
    std::cout << "========================================" << std::endl;

    std::cout << "\n-- Creating original DeepArray of size 4 --" << std::endl;
    DeepArray original(4);
    original.print();

    std::cout << "\n-- Copy-constructing 'copy' from 'original' --" << std::endl;
    DeepArray copy(original);           // invokes copy constructor
    copy.print();

    std::cout << "\n-- Modifying copy (setting index 0 to 999) --" << std::endl;
    copy.set(0, 999);

    std::cout << "  original: ";
    original.print();
    std::cout << "  copy:     ";
    copy.print();
    std::cout << "  (original is unchanged -- deep copy works!)" << std::endl;

    std::cout << "\n========================================" << std::endl;
    std::cout << "3. COPY CONSTRUCTOR VIA PASS-BY-VALUE" << std::endl;
    std::cout << "========================================" << std::endl;
    passByValue(original);

    std::cout << "\n========================================" << std::endl;
    std::cout << "4. DESTRUCTORS RUN (reverse order)" << std::endl;
    std::cout << "========================================" << std::endl;

    return 0;
}

// Expected output (addresses will vary):
// ========================================
// 1. SHALLOW COPY PROBLEM (dangerous!)
// ========================================
// Creating original ShallowArray of size 3:
//   ShallowArray constructed, data @ 0x...
//   [0, 10, 20]
//
//   NOTE: If we copied 'original' with the default copy
//   constructor, BOTH objects would share the same pointer.
//   When they destruct, the memory is freed TWICE -> undefined behaviour!
//   ShallowArray destructor, freeing data @ 0x...
//
// ========================================
// 2. DEEP COPY SOLUTION
// ========================================
//
// -- Creating original DeepArray of size 4 --
//   DeepArray constructed, data @ 0x...
//   [0, 10, 20, 30]
//
// -- Copy-constructing 'copy' from 'original' --
//   DeepArray COPY constructed, new data @ 0x... (source was @ 0x...)
//   [0, 10, 20, 30]
//
// -- Modifying copy (setting index 0 to 999) --
//   original:   [0, 10, 20, 30]
//   copy:       [999, 10, 20, 30]
//   (original is unchanged -- deep copy works!)
//
// ========================================
// 3. COPY CONSTRUCTOR VIA PASS-BY-VALUE
// ========================================
//   DeepArray COPY constructed, new data @ 0x... (source was @ 0x...)
//   Inside passByValue, array contains:  [0, 10, 20, 30]
//   DeepArray destructor, freeing data @ 0x...
//
// ========================================
// 4. DESTRUCTORS RUN (reverse order)
// ========================================
//   DeepArray destructor, freeing data @ 0x...
//   DeepArray destructor, freeing data @ 0x...
Run command
g++ -std=c++98 -Wall -Wextra -o example copy_constructor.cpp