C++ Copy Constructor



Copy Constructor

The copy constructor is a constructor that creates an object by initializing it with an object of the same class which has been created previously. The copy constructor is used to −

  • Initialize one object from another of the same type.
  • Copy an object to pass it as an argument to a function.
  • Copy an object to return it from a function.

If a copy constructor is not defined in a class, the compiler itself defines one.If the class has pointer variables and has some dynamic memory allocations, then it is a must to have a copy constructor.

Syntax

The most common form of copy constructor is shown here −

classname (const classname &obj) {
   // body of constructor
}

Here, obj is a reference to an object that is being used to initialize another object.

Example of Copy Constructor

The following example demonstrates the use of the copy constructor:

#include <iostream>

using namespace std;

class Line {

   public:
      int getLength( void );
      Line( int len );             // simple constructor
      Line( const Line &obj);  // copy constructor
      ~Line();                     // destructor

   private:
      int *ptr;
};

// Member functions definitions including constructor
Line::Line(int len) {
   cout << "Normal constructor allocating ptr" << endl;
   
   // allocate memory for the pointer;
   ptr = new int;
   *ptr = len;
}

Line::Line(const Line &obj) {
   cout << "Copy constructor allocating ptr." << endl;
   ptr = new int;
   *ptr = *obj.ptr; // copy the value
}

Line::~Line(void) {
   cout << "Freeing memory!" << endl;
   delete ptr;
}

int Line::getLength( void ) {
   return *ptr;
}

void display(Line obj) {
   cout << "Length of line : " << obj.getLength() <<endl;
}

// Main function for the program
int main() {
   Line line(10);

   display(line);

   return 0;
}

When the above code is compiled and executed, it produces the following result −

Normal constructor allocating ptr
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
Freeing memory!

Copy Constructor to Create New Object

You can create a new object by using the existing object through the concept of a copy constructor.

In the following example, the copy constructor is used to create a new object as a copy of an existing object.

Example

Let us see the same example but with a small change to create another object using existing object of the same type −

#include <iostream>

using namespace std;

class Line {
   public:
      int getLength( void );
      Line( int len );             // simple constructor
      Line( const Line &obj);  // copy constructor
      ~Line();                     // destructor

   private:
      int *ptr;
};

// Member functions definitions including constructor
Line::Line(int len) {
   cout << "Normal constructor allocating ptr" << endl;
   
   // allocate memory for the pointer;
   ptr = new int;
   *ptr = len;
}

Line::Line(const Line &obj) {
   cout << "Copy constructor allocating ptr." << endl;
   ptr = new int;
   *ptr = *obj.ptr; // copy the value
}

Line::~Line(void) {
   cout << "Freeing memory!" << endl;
   delete ptr;
}

int Line::getLength( void ) {
   return *ptr;
}

void display(Line obj) {
   cout << "Length of line : " << obj.getLength() <<endl;
}

// Main function for the program
int main() {

   Line line1(10);

   Line line2 = line1; // This also calls copy constructor

   display(line1);
   display(line2);

   return 0;
}

When the above code is compiled and executed, it produces the following result −

Normal constructor allocating ptr
Copy constructor allocating ptr.
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
Freeing memory!
Freeing memory!

Implicit vs. Explicit Copy Constructors

In C++, there are two types of Copy Constructors that's Implicit and Explicit. Here we will discuss the difference between these two.

Implicit Copy Constructor

If the user doesn't define their own copy constructor, then the compiler automatically provides an implicit copy constructor. It performs a shallow copy of the object, which means that it copies the values of each member of the object to the new object.

When is the Implicit Copy Constructor called?

  • When a user passes an object by value to a function.
  • When the user returns an object by value from a function.
  • When the user initializes an object with another object of the same type (copy initialization).

Explicit (User-Defined) Copy Constructor

It is the user-defined constructor. This gives you access to customize the copy behavior like creating a deep copy instead of the default shallow copy.

Example

Here is the example for both explicit and implicit copy constructors in C++:

#include <iostream>
using namespace std;

class MyClass {
 private:
  int value;

 public:
  // Constructor
  MyClass(int v) : value(v) {}

  // Explicit Copy Constructor
  MyClass(const MyClass& other) : value(other.value) {
    cout << "Explicit Copy Constructor called" << endl;
  }

  void display() const { cout << "Value: " << value << endl; }
};

void processValue(MyClass obj) {
  // Implicit copy constructor will be called here
  obj.display();
}

int main() {
  MyClass obj1(10);     // Constructor called
  MyClass obj2 = obj1;  // Explicit copy constructor called
  obj1.display();
  obj2.display();

  processValue(obj1);  // Implicit copy constructor called
  return 0;
}

When the above code is compiled and executed, it produces the following result −

Explicit Copy Constructor called
Value: 10
Value: 10
Explicit Copy Constructor called
Value: 10

Rule of Three/Five

The Rule of Three and Rule of Five suggest while defining a copy constructor (ClassName(const ClassName& other)) you should also define:

The Rule of Three and Rule of Five suggest while defining a copy constructor (ClassName(const ClassName& other)) you should also define:

  • Rule of Three:
    • destructor (~ClassName()).
    • And copy assignment operator (ClassName& operator=(const ClassName& other)), to ensure that memory is correctly managed.
  • Rule of Five:
    • move constructor (ClassName(ClassName&& other)).
    • move assignment operator (ClassName& operator=(ClassName&& other))". 

These special member functions are necessary for the proper management of dynamic memory and other and other resources like file handling or network connections in a class.

Deep Copy vs. Shallow Copy

In C++, deep copy and shallow copy are different ways of copying objects and they are important when a class involves dynamic memory management.

1. Shallow Copy

It occurs when an object is copied in such a way that both original and copied objects share the same resources. This means that the copy constructor or copy assignment operator simply copies the values of data members (like pointers), without allocating new memory or making independent copies of the resources.

Example

#include <iostream>
using namespace std;

class MyClass {
 private:
  int* data;  // Pointer to an integer

 public:
  // Constructor
  MyClass(int value) {
    data = new int(value);  // Allocate memory
  }

  // Shallow Copy Constructor
  MyClass(const MyClass& other) {
    data = other.data;  // Copy pointer only
  }

  // Destructor
  ~MyClass() {
    delete data;  // Free memory
  }

  // Display the value
  void showData() const { cout << "Data: " << *data << endl; }
};

int main() {
  MyClass obj1(42);     // Create an object
  MyClass obj2 = obj1;  // Use shallow copy constructor

  obj1.showData();
  obj2.showData();

  return 0;
}

When the above code is compiled and executed, it produces the following result −

Data: 42
Data: 42
free(): double free detected in tcache 2

2. Deep Copy

It occurs when an object is copied by allocating new memory for its own copy of the resources, making sure that the original and copied object is completely independent. Avoids double-free errors or dangling pointers.

Example

#include <iostream>
using namespace std;

class MyClass {
 private:
  int* data;  // Pointer to an integer

 public:
  // Constructor: Dynamically allocate memory 
  // and initialize with value
  MyClass(int value) { data = new int(value); }

  // Deep Copy Constructor
  // Allocates new memory and copies the value
  MyClass(const MyClass& other) { data = new int(*other.data); }

  // Destructor to clean up memory
  ~MyClass() { delete data; }

  // Display the value
  void showData() const { cout << "Data: " << *data << endl; }
};

int main() {
  MyClass obj1(42);     // Create an object
  MyClass obj2 = obj1;  // Use deep copy constructor

  obj1.showData();  // Display data from obj1
  obj2.showData();  // Display data from obj2

  return 0;
}

When the above code is compiled and executed, it produces the following result −

Data: 42
Data: 42
Advertisements