Curriculum
Course: Complete C++ Programming Course
Login

Curriculum

Complete C++ Programming Course

Text lesson

Understanding Copy Constructor in C++

In this lesson, you will learn

  • Copy Constructor
  • Examples – Copy Constructor

 

Copy Constructor

  • A copy constructor is a special constructor that allows you to create a new object as a copy of an existing object of the same class.
  • When a copy of an object is made, the copy constructor is called to handle the copying of data members from the source object to the newly created object.
  • It’s used to initialize an object using another object’s data.

 

Syntax of a Copy Constructor

A copy constructor takes a reference to an object of the same class as an argument:

ClassName(const ClassName &obj)
{
    member1 = obj.member1;
    ...
    
    //Other Members
}

Here, obj is a reference to the object that is being copied. The const qualifier is optional, but is added to prevent accidental modification of the object.

 

Invoking the Copy Constructor

The copy constructor is invoked using the following syntax.

Method-1:

Class-Name p2(p1); An object p2 is initialized by the value of p1.

Method-2:

Class-Name p2 = p1;

Note: A copy constructor takes a reference to an object of the same class as an argument.

 

Example 1: Copy Constructor

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

class Book {
private:
    string title;
    string author;
    int pages;

public:
    // Parameterized Constructor
    Book(string t, string a, int p) : title(t), author(a), pages(p) {}

    // Copy Constructor
    Book(const Book &b) {
        title = b.title;
        author = b.author;
        pages = b.pages;
        cout << "Copy constructor called for Book: " << title << endl;
    }

    // Function to display book details
    void display() const {
        cout << "Title: " << title << ", Author: " << author 
            << ", Pages: " << pages << endl;
    }
};

int main() {
    // Create a Book object using the parameterized constructor
    Book book1("1984", "George Orwell", 328);

    // Use the copy constructor to create a new Book object
    Book book2 = book1; // Copy constructor is called here

    // Display details of both books
    cout << "Book 1 Details:" << endl;
    book1.display();

    cout << "Book 2 Details:" << endl;
    book2.display();

    return 0;
}

Output:

Copy constructor called for Book: 1984
Book 1 Details:
Title: 1984, Author: George Orwell, Pages: 328 
Book 2 Details:
Title: 1984, Author: George Orwell, Pages: 328


Important Note!

If you do not provide the user-defined copy constructor, then the compiler creates it automatically.

Let’s see the following examples where we are not adding a user-defined copy constructor in the class Box.

 

Example 2: Default Copy Constructor

If you don’t define a copy constructor, the compiler provides a default one, which performs a shallow copy of the object.

Let’s see this with an example:

#include<iostream>
using namespace std;

class Box {
public:
    int length;
    Box(int l) : length(l) {}
};

int main() {
    Box box1(10);      // Create an object
    Box box2 = box1;   // Copy constructor called

    cout << "Box1 length: " << box1.length <<&box1<< endl;
    cout << "Box2 length: " << box2.length <<&box2 << endl;

    return 0;
}

Output:

Box1 length: 10
Box2 length: 10

In this example, box2 is created as a copy of box1, and the default copy constructor simply copies the value of length.

 

Is the above code a shallow copy or a deep copy?

  • Shallow copy issues happen only when you have pointers or dynamically allocated memory inside your class.

  • Since Box has no pointers (only an int), the concept of shallow vs. deep doesn’t really apply here.

  • Technically, this is just a member-wise copy — and it behaves like a deep copy because int is a value type.

If you later added a pointer member like:

int* length;

then the default/your copy constructor as written would be a shallow copy, and you’d need to write a deep copy constructor to allocate new memory.

 

What is the Need for a User-Defined Copy Constructor?

If you don’t define your own copy constructor, the compiler provides a default one, which performs a shallow copy of the object(In case of a pointer variable only). Sometimes, the default shallow copy isn’t enough, especially if the class manages dynamic memory.

In such cases, you need to define a custom copy constructor.

However, we need to define our own copy constructor only when your class or data structure handles dynamically allocated resources, such as memory, file handles, or network connections, and it is known as the deep copy in C++.

 

What is a Shallow Copy?

A shallow copy creates a new object but does not allocate new memory for the resources the object manages.

Shallow Copy means that only the pointers will be copied, not the actual resources that the pointers are pointing to. This can lead to dangling pointers if the original object is deleted.

Characteristics:

  • Pointer Copy: Only the addresses of dynamically allocated memory are copied, not the actual data.
  • Shared Resources: Both the original and the copied objects refer to the same memory locations.
  • Risk of Issues: If one object modifies or frees the shared resources, it can affect the other object. Problems like double deletion or unexpected modifications can occur

 

Example – Shallow Copy of an Object’s Data

#include<iostream>
using namespace std;

class ShallowCopyExample {
private:
    int* data;

public:
    // Constructor
    ShallowCopyExample(int value) {
        data = new int(value);  // Allocate memory and initialize
        cout << "Constructor: Memory allocated at " << data << endl;
    }

    // Shallow Copy Constructor
    ShallowCopyExample(const ShallowCopyExample &obj) {
        data = obj.data;  // Shallow copy: copy the pointer, not the data
        cout << "Copy Constructor (Shallow): Pointer copied, address " << data << endl;
    }

    // Destructor
    ~ShallowCopyExample() {
        cout << "Destructor: Memory freed at " << data << endl;
        delete data;  // Free the allocated memory
    }

    // Function to display the value and its memory address
    void display() const {
        cout << "Value: " << *data << ", Address: " << data << endl;
    }
};

int main() {
    ShallowCopyExample obj1(100);   // Constructor is called
    ShallowCopyExample obj2 = obj1; // Shallow copy constructor is called

    // Display values and their addresses
    cout << "obj1: ";
    obj1.display();
    
    cout << "obj2: ";
    obj2.display();

    return 0;
}


Output

Constructor: Memory allocated at 0x21d8ce13860
Copy Constructor (Shallow): Pointer copied, address 0x21d8ce13860
obj1: Value: 100, Address: 0x21d8ce13860
obj2: Value: 100, Address: 0x21d8ce13860
Destructor: Memory freed at 0x21d8ce13860
Destructor: Memory freed at 0x21d8ce13860

 

Explanation of the Output:

  • Memory Addresses:

    • The constructor prints the memory address allocated for obj1.
    • The shallow copy constructor prints the same memory address for obj2, indicating that both obj1 and obj2 share the same memory location.
    • The display function confirms that both obj1 and obj2 have the same memory address for their data members.
  • Destructor Output:

    • The destructor is called twice (once for each object), leading to double deletion because both obj1 and obj2 share the same memory. This can cause undefined behavior, including potential crashes.

 

What is a Deep Copy?

A deep copy creates a new object and allocates new memory for the resources. It copies the actual data from the original object to the new object. Thus, each object has its own separate memory addresses for its resources.

Deep copy is possible only with a user-defined copy constructor. In a user-defined copy constructor, we make sure that pointers (or references) of copied objects point to a new copy of the dynamic resource allocated manually in the copy constructor using new operators.

Characteristics:

  • Data Copy: Copies the actual data, not just the pointers.
  • Independent Resources: Each object manages its own memory, so changes or deletions in one object do not affect the other.
  • Avoids Issues: Reduces the risk of problems like double deletion or unintended modifications.

Example: Deep Copy Example

#include<iostream>
using namespace std;

class DeepCopyExample {
private:
    int* data;

public:
    // Constructor
    DeepCopyExample(int value) {
        data = new int(value);  // Allocate memory and initialize
        cout << "Constructor: Memory allocated at " << data << endl;
    }

    // Deep Copy Constructor
    DeepCopyExample(const DeepCopyExample &obj) {
        data = new int(*obj.data);  // Allocate new memory and copy the value
        cout << "Copy Constructor: Memory allocated at " << data << endl;
    }

    // Destructor
    ~DeepCopyExample() {
        cout << "Destructor: Memory freed at " << data << endl;
        delete data;  // Free the allocated memory
    }

    // Function to display the value and its memory address
    void display() const {
        cout << "Value: " << *data << ", Address: " << data << endl;
    }
};

int main() {
    DeepCopyExample obj1(100);   // Constructor is called
    DeepCopyExample obj2 = obj1; // Copy constructor is called

    // Display values and their addresses
    cout << "obj1: ";
    obj1.display();
    
    cout << "obj2: ";
    obj2.display();

    return 0;
}


Output:

Constructor: Memory allocated at 0x18bd99c3870
Copy Constructor: Memory allocated at 0x18bd99c3890
obj1: Value: 100, Address: 0x18bd99c3870
obj2: Value: 100, Address: 0x18bd99c3890
Destructor: Memory freed at 0x18bd99c3890
Destructor: Memory freed at 0x18bd99c3870


Explanation of the Output:

  • Memory Addresses:
    • The constructor prints the memory address allocated for obj1.
    • The copy constructor prints a different memory address for obj2, showing that a separate memory allocation occurred for the deep copy.
    • The display function confirms that obj1 and obj2 have different memory addresses for their data members.
  • Destructor Output:
    • The destructors for obj1 and obj2 free the respective memory addresses, ensuring that there are no memory leaks.

 

 


End of the lesson….enjoy learning

 

 

Student Ratings and Reviews

 

5.0
5.0 out of 5 stars (based on 3 reviews)
Excellent
Very good
Average
Poor
Terrible

 

 

2 October 2024

excellent

20 September 2024

nice

14 September 2024

Good

 

 

Submit a Review