C++ Class Copy Constructor

Introduction

When we initialize an object with another object of the same class, we invoke a copy constructor.

If we do not supply our copy constructor, the compiler generates a default copy constructor that performs the so-called shallow copy.

Example:

#include <iostream> 

class MyClass //from www. j a v  a  2 s  . c  o m
{ 
private: 
    int x, y; 
public: 
    MyClass(int xx, int yy) : x{ xx }, y{ yy } 
    { 
    } 
}; 

int main() 
{ 
    MyClass o1{ 1, 2 }; 
    MyClass o2 = o1; // default copy constructor invoked 
} 

In this example, we initialize the object o2 with the object o1 of the same type.

This invokes the default copy constructor.

We can provide our own copy constructor.

The copy constructor has a special parameter signature of MyClass(const MyClass& rhs).

Example of a user-defined copy constructor:

#include <iostream> 

class MyClass //from  www . j  a  va2 s  .  c o  m
{ 
private: 
    int x, y; 
public: 
    MyClass(int xx, int yy) : x{ xx }, y{ yy } 
    { 
    } 

    // user defined copy constructor 
    MyClass(const MyClass& rhs) 
         : x{ rhs.x }, y{ rhs.y } // initialize members with other object's   
          // members 
    { 
        std::cout << "User defined copy constructor invoked."; 
    } 
}; 

int main() 
{ 
    MyClass o1{ 1, 2 }; 
    MyClass o2 = o1; // user defined copy constructor invoked 
} 

Here we defined our own copy constructor in which we explicitly initialized data members with other objects data members, and we print out a simple message in the console / standard output.

Please note that the default copy constructor does not correctly copy members of some types, such as pointers, arrays, etc.

In order to properly make copies, we need to define our own copy logic inside the copy constructor.

This is referred to as a deep copy.

For pointers, for example, we need both to create a pointer and assign a value to the object it points to in our user-defined copy constructor:

#include <iostream> 

class MyClass /*w ww .  ja v  a 2s.  c o m*/
{ 
private: 
    int x; 
    int* p; 
public: 
    MyClass(int xx, int pp) 
         : x{ xx }, p{ new int{pp} } 
    { 
    } 

MyClass(const MyClass& rhs) 
         : x{ rhs.x }, p{ new int {*rhs.p} } 
    { 
        std::cout << "User defined copy constructor invoked."; 
    } 
}; 

int main() 
{ 
    MyClass o1{ 1, 2 }; 
    MyClass o2 = o1; // user defined copy constructor invoked 
} 

Here we have two constructors, one is a user-provided regular constructor, and the other is a user-defined copy constructor.

The first constructor initializes an object and is invoked here:

MyClass o1{ 1, 2 }; 

in our main function.

The second, the user-defined copy constructor is invoked here:

MyClass o2 = o1; 

This constructor now properly copies the values from both int and int* member fields.

In this example, we have pointers as member fields.

If we had left out the user- defined copy constructor, and relied on a default copy constructor only the int member field would be properly copied, the pointer would not.

In this example, we rectified that.

In addition to copying, there is also a move semantic, where data is moved from one object to the other.

This semantic is represented through a move constructor and a move assignment operator.




PreviousNext

Related