C++ Copy Constructors

In C++, a copy constructor is a special member function that is used to create a new object as a copy of an existing object. It is called when a new object is initialized with an existing object of the same class. The copy constructor allows for the creation of a deep copy of the object, ensuring that all the member variables are correctly copied.

The syntax for a copy constructor is:

class ClassName {
    ClassName(const ClassName& obj) {
        // Copy constructor implementation
    }
};

Here, ClassName refers to the name of the class for which the copy constructor is being defined. The parameter const ClassName& obj is a reference to the object that needs to be copied.

Let’s understand the concept of copy constructors with a few examples:

Example 1: Copying Primitive Data Types

#include 

class Number {
private:
    int num;
public:
    Number(int n) {
        num = n;
    }
    Number(const Number& obj) {
        num = obj.num;
    }
    int getNumber() {
        return num;
    }
};

int main() {
    Number num1(10);
    Number num2 = num1; // Copy constructor called
    
    std::cout << "num1: " << num1.getNumber() << std::endl;
    std::cout << "num2: " << num2.getNumber() << std::endl;
    
    return 0;
}

In this example, we have a class Number with a member variable num. The copy constructor is defined to copy the value of num from one object to another. When we create num2 as a copy of num1, the copy constructor is called, and num2 is initialized with the same value as num1.

The output of this program will be:

num1: 10
num2: 10

Example 2: Copying Objects with Dynamic Memory Allocation

#include 

class String {
private:
    char* str;
public:
    String(const char* s) {
        int length = strlen(s);
        str = new char[length + 1];
        strcpy(str, s);
    }
    String(const String& obj) {
        int length = strlen(obj.str);
        str = new char[length + 1];
        strcpy(str, obj.str);
    }
    ~String() {
        delete[] str;
    }
    const char* getString() {
        return str;
    }
};

int main() {
    String s1("Hello");
    String s2 = s1; // Copy constructor called
    
    std::cout << "s1: " << s1.getString() << std::endl;
    std::cout << "s2: " << s2.getString() << std::endl;
    
    return 0;
}

In this example, we have a class String that manages a dynamically allocated character array. The copy constructor is defined to allocate a new memory block and copy the contents of the source object’s string. This ensures that each object has its own separate memory for the string.

The output of this program will be:

s1: Hello
s2: Hello

It is important to note that when a class contains pointers or dynamically allocated memory, a copy constructor should be defined to perform a deep copy. Otherwise, both objects will end up pointing to the same memory location, leading to issues when one object is modified.

Example 3: Copy Constructor in Inheritance

#include 

class Base {
protected:
    int value;
public:
    Base(int v) {
        value = v;
    }
    Base(const Base& obj) {
        value = obj.value;
    }
    int getValue() {
        return value;
    }
};

class Derived : public Base {
public:
    Derived(int v) : Base(v) {}
};

int main() {
    Derived d1(5);
    Derived d2 = d1; // Copy constructor called
    
    std::cout << "d1: " << d1.getValue() << std::endl;
    std::cout << "d2: " << d2.getValue() << std::endl;
    
    return 0;
}

In this example, we have a base class Base and a derived class Derived. The copy constructor is defined in the base class, and it is called when creating a copy of a derived object. The derived object’s base part is copied using the copy constructor of the base class.

The output of this program will be:

d1: 5
d2: 5

By defining a copy constructor, you can control how objects are copied and ensure that the new object is a true copy of the original. This is especially important when dealing with dynamically allocated memory or when working with inheritance.

Remember to always define a copy constructor when necessary to avoid unexpected behavior and ensure the correct functioning of your C++ classes.

Scroll to Top