blog icon indicating copy to clipboard operation
blog copied to clipboard

C++ Copy Constructors

Open qingquan-li opened this issue 1 year ago • 0 comments

Concept: A copy constructor is a special constructor that is called whenever a new object is created and initialized with another object's data.

  • Special constructor used when a newly created object is initialized to the data of another object of same class:

    Rectangel r2 = r1;
    
  • Default copy constructor copies field-to-field, using memberwise assignment.

    (Memberwise assignment: use = to assign one object to another, or to initialize an object with an object's data, e.g., instance2 = instance1;)

    In C++, when you do not provide a copy constructor for your class, the compiler generates one for you.

    SampleClass obj1(10, 20);
    // This calls the default copy constructor.
    // This operation is essentially a shallow copy (copy the address of the object).
    SampleClass obj2 = obj1;
    

A copy constructor is called when:

  • An object is initialized from an object of the same class.
  • An object is passed by value to a function.
  • An object is returned using a return statement from a function.

1. Copy Constructor Example

A copy constructor is a member function that initializes an object using another object of the same class. It is used to perform a deep copy or sometimes a shallow copy of objects.

class SimpleClass {
private:
    int num;

public:
    // Default constructor
    // SimpleClass() : num(0) {} // explicitly initialize num to 0
    SimpleClass() {}

    // Parameterized constructor
    SimpleClass(int n) {
        num = n;
    }

    // Copy constructor
    // `const`: cannot modify the object obj inside the copy constructor.
    // `&`: pass by reference rather than by value (make a copy).
    //      If pass it by value, cannot access the original object's private members.
    SimpleClass(const SimpleClass &obj) {
        num = obj.num;
    }

    void showNum() const {
        std::cout << "Number: " << num << std::endl;
    }
};

2. Deep Copy vs Shallow Copy

Since the default copy constructor is essentially a shallow copy (copy the address of the object). We need to define a copy constructor with objects containing dynamic memory.

// Deep Copy with Dynamic Memory Allocation

class DeepCopyClass {
private:
    int *numPtr;

public:
    // Default constructor
    DeepCopyClass() {
        numPtr = new int;
        *numPtr = 0;
    }

    // Parameterized constructor
    DeepCopyClass(int n) {
        numPtr = new int;
        *numPtr = n;
    }

    // Copy constructor for deep copy
    // deep copy: copy the value of the object, not the address
    // shallow copy: copy the address of the object
    DeepCopyClass(const DeepCopyClass &obj) {
        numPtr = new int; // allocate memory for the new object
        *numPtr = *(obj.numPtr); // copy the value of the object
    }

    ~DeepCopyClass() {
        delete numPtr;
    }

    void showNum() const {
        std::cout << "Number: " << *numPtr << std::endl;
    }
};
// Shallow Copy (not recommended for dynamic memory)

class ShallowCopyClass {
private:
    int *numPtr;

public:
    // Default constructor
    ShallowCopyClass() {
        numPtr = new int;
        *numPtr = 0;
    }

    // Parameterized constructor
    ShallowCopyClass(int n) {
        numPtr = new int;
        *numPtr = n;
    }

    // Copy constructor for shallow copy
    ShallowCopyClass(const ShallowCopyClass &obj) {
        numPtr = obj.numPtr; // copy the address of the object
    }

    ~ShallowCopyClass() {
        delete numPtr;
    }

    void showNum() const {
        std::cout << "Number: " << *numPtr << std::endl;
    }
};

qingquan-li avatar Oct 09 '23 03:10 qingquan-li