Copy constructor and Copy Assignment operator in c++

This article contains explanations on:
- overloaded copy constructors
- overloaded copy assignment operators
- shallow copy vs deep copy

1. We need to keep in mind that there are certain functions that the C++ compiler automatically provides for us. That is, even if we do not provide these functions explicitly when we write our classes, the compiler will provide them by default. Among them are:
- copy constructor
- default constructor
- copy assignment operator
- destructor
The constructor and destructor which are provided to us by the compiler don’t actually do anything special. They just sit there and do nothing.
Note that the default constructor provided automatically by the compiler does not take any parameters. i.e. it is parameterless. (which is quite obvious, since it actually does nothing so as to require any parameters.)

2. Another interesting thing to note is that if we provide any parameterized constructor (i.e. a constructor that takes one or more parameters) the default parameterless constructor provided automatically by the compiler vanishes.
Take this example:
class MyClass
{
public:
MyClass (int value) //parameterized constructor provided by us.
{
x = value;
}
private:
int x;
};
Now, as we can see we have provided a parameterized constructor by ourselves for our class.
So, the compiler responds by not providing to us by default any parameterless constructor.So, if we write in the main program, something like:
MyClass obj; //error
The compiler would generate an error, because the way we are trying to instantiate the class in the above statement requires a parameterless constructor, but neither have we explicitly provided one, nor has the compiler automatically provided one.
Hence, the rule to remember is this: if we would have any need for a parameterless constructor, either provide no constructor at all (i.e. not even a parameterized constructor) so that the compiler can provide us with a parameterless constructor automatically or, if you are anyway going to provide a parameterized constructor, make sure that you also provide a parameterless constructor.
i.e. in our example, we can modify that as follows:
class MyClass
{
public :
MyClass (int value)
{
x = value;
}
MyClass ( )
{
x = 10;
}
private:
int x;
};

3. Assignment vs copy construction
Copy constructors are used in a variety of situations. Two of them are as follows:
- X obj1 (obj2); //constructing obj1 as a copy of obj2.... copy constructor called.
- X obj1 = obj2; // again, same as above.
In contrast, in the following code segment, the copy assignment operator is invoked:
X obj1;
obj1 = obj2; //assignment, not construction.

4. The copy constructor and the copy assignment operator provided to us by default by the compiler also do what are they are supposed to do - i.e. create a new copy of an object, or assign one object to another, in the most simple way possible.
They do a shallow copy and not a deep copy.
Let us see that through an example.
class X
{
public:
X (int value1, value2)
{
data1 = value1;
data2 = value2;
}
X ( )
{
data1 = data2 = 10;
` }/
/some functions here, but no copy constructor or copy assignment operator.
private:
int data1;
int data2;
};
Now, let us see what happens in the following statements:
X obj1 (10, 20); // obj1 is constructed using parameterized constructor of X.
X obj2 (obj1); //here, we are invoking copy constructor of X to construct obj2 as a
copy of obj1.
But, we have not explicitly provided any copy constructor. So, the copy
constructor provided by the compiler is called.
It constructs obj2 by copying:
- obj1.data1 into obj2.data1
- obj1.data2 into obj2.data2
Similarly, if we have the following statements:
X obj3 (100, 200); //parameterized constructor called.
obj3 = obj1; //copy assignment operator called. Since we do not provide one,
compiler’s version is used.
The compiler’s copy assignment operator just copies obj1.data1 into
obj3.data1 and obj1.data2 into obj3.data2.

5. When does the compiler’s version of copy constructor and copy
assignment operator fail?
Answer: When what we want is a deep copy and not a shallow copy. That
is, when we have any member variable which is a pointer.
Let us see the following example:
class MyClass
{
public:
//some functions, but no copy constructor or copy assignment operator.
private:
int data;
int* ptr;
};
Now, as we can see, the member variable of our class contains a pointer
variable. This means there will be trouble if we call the compiler’s version of copy constructor and assignment operator.
Take, for instance, what happens when we write:
MyClass obj2 (obj1); //assume that obj1 is already constructed earlier.
Now, the compiler’s copy constructor will perform the following operations:
obj2.data = obj1.data;
and obj2.ptr = obj1.ptr;
The statement obj2.data = obj1.data is okay. There is no problem with that. But the statement obj2.ptr = obj1.ptr is trouble for us.
What that statement means is that both obj2.ptr and obj1.ptr will point to
the same place in memory. That is not what we want. This is what is known as shallow copy -- simply copying the pointers, and not the underlying data being pointed to by the pointers.
What we wanted is this: suppose obj1.ptr was currently pointing to an
integer whose value is 1000. Then, we want that obj2.ptr should point to some  other location in memory, where also the value of the integer stored is 1000. This is what is known as deep copy -- i.e. copying the underlying data being pointed to by a pointer, and creating a new pointer to point to that new location.
Similarly, the compiler’s copy assignment operator performs a shallow copy
instead of a deep copy.
So, to summarize, the compiler’s copy constructor and copy assignment
operator fail when what we what is a deep copy and not a shallow copy. i.e. when we have a pointer member variable in our class.

6. Overloading the copy constructor:
We can provide our own copy constructor for our class. When we do so, it is
said that we are overloading the copy constructor. When we provide our own copy constructor, obviously the compiler’s version of copy constructor will no longer be called.
Let us see how to write a copy constructor for our class.
class MyClass
{
public:
MyClass (const MyClass& param); //copy constructor
private:
int data;
int* ptr;
};
As we can see, the copy constructor takes as parameter the object which we
want to copy. In general, we will always use a parameter which is reference-to-const as in the example above.
Now, we need to define the copy constructor:
All we do is to copy all member variables of our parameter, making sure
that we are doing a deep copy.
MyClass :: MyClass (const MyClass& param)
{
//copy data member variable
data = param.data; //also can be written as : this -> data = param.data;
//deep copy ptr member variable variable
// two steps we need to do for this
// 1. create a new pointer using new operator
// 2. that newly created pointer should point to a value which is the same as
that pointed to by param.ptr. i.e. if param.ptr is pointing to 10000, then this new pointer
should also to 10000.
ptr = new int;
*ptr = *param.ptr;
//or, we could combine both these statements into one: ptr = new int (*param.ptr);
}
--------------
So, we saw the following facts about how to write our own copy
constructor:
(a). The parameter for the copy constructor is const ClassName& param. (ie reference to const)
(b) Copy all member variables of param into our (this) object, keeping in
mind to do deep copy whenever pointer variables are involved.
-------------

7. How to write overloaded copy assignment operator?
There are 4 things to remember when writing copy assignment operator:
(two of them are the same as for copy constructor)
(a) The parameter for the copy constructor is const ClassName& param. (i.e.
reference-to-const)
(b) Copy all member variables of param into our (this) object, keeping in mind to do deep copy whenever pointer variables are involved.
( c) Return type of copy assignment operator is ClassName & (i.e. reference
type)
(d) Guard against self copy (i.e. statements like : obj1 = obj1; )
Let us look at an overloaded copy assignment operator through example:
class MyClass
{
public:
MyClass& operator= (const MyClass& param) ;
//Note the following:
// return type is : MyClass&
// parameter type is : const MyClass& (this is usually how objects are generally
passed as parameters to most functions. So, nothing special here.
// name of function : operator=.
private:
int data;
int* ptr;
};
Now, let us write the implementation for the assignment operator that we have
overloaded:
MyClass& operator= (const MyClass& param)
{
int* temp; // a temporary pointer
if (this != &param )
//this is very imp --- guarding against self assignment (i.e. obj1 = obj1)
// if LHS and RHS of assignment are the same object, then don’t do anything.
{
//copy the member variable “int data”
data = param.data; //or, this->data = param.data;
//deep copy member variable “int* ptr”
//take care to delete old copy of this->ptr
temp = new int (*param.ptr); //temp is a newly created pointer.
//delete the old this->ptr;
delete ptr;
//assign ptr to temp
ptr = temp;
}
//finally return (*this)
return *this;
}

Source:

0 comments:

Post a Comment