Tuesday, 26 November 2013

COPY CONSTRUCTOR VS OVERLOADING ASSIGNMENT OPERATING

Assignment operator:
The assignment operator is used to copy the values from one object to another already existing object.

Copy constructor:
A copy constructor is a special constructor that initializes a new object from an existing object.

#include <iostream>
using namespace std;

class Base{
public:
    Base(){}
    Base(const Base &t){
      cout << "Copy constructor" << endl;
   }
   Base& operator = (const Base &t)   {
      cout << "Assignment operator " << endl;
      return *this;
   }
};

int main(void){
    Base objA, objB;
    objA = objB;        /* Assignment operator is called */
    Base objC = objA;   /* Copy constructor is called */
    system("pause");
    return 0;

}

output:









There are three general cases where the copy constructor is called instead of the assignment operator:
  1. When instantiating one object and initializing it with values from another object (as in the example above).
  2. When passing an object by value.
  3. When an object is returned from a function by value.

Case 1: It is already shown in the previous example.

case 2: Passing an object by value.

#include <iostream>
using namespace std;

class Base{
public:
    Base(){}
    Base(const Base &t){
      cout << "Copy constructor is called" << endl;
   }
};

void callByValue(Base obj){
    /* It will result in creating a temporary object and assigning of the obj to temp obj */
    /* Hence calls copy constructor */
}

int main(void){
    Base objA;
    /* Passing an object (objA) by value in function callByValue */
    callByValue(objA);
    return 0;
}

output:



Case 3: Returning object by Value.

#include <iostream>
using namespace std;

class Base{
public:
    Base(){}
    Base(const Base &t){
      cout << "Copy constructor is called" << endl;
   }
};

Base returnByValue(){
    Base objTemp;
    /* Passing by value results calling of copy constructor */
    return objTemp;
}

int main(void){
    Base objA;
    objA = returnByValue();
    return 0;
}

output:

In all the above mentioned cases a new variable needs to be created which results in the use of copy constructor.


Why copy constructor or overloaded assignment operator ?

Compiler normally provide a default copy constructor and default assignment operator and it uses a method known as Shallow copy (also known as member wise copy).
Shallow copy
Each member of the class individually using the assignment operator. The standard pointer assignment operator just copies the address of the pointer — it does not allocate any memory or copy the contents being pointed to!
Both the objects in this case points to the same memory location and hence any modification or deletion of the memory by one object may lead to lots of trouble. When classes are simple (e.g. do not contain any dynamically allocated memory), shallow copy works very well.

Lets see example what we discussed above. Consider the program below.

#include <iostream>
using namespace std;

class Base{
public:
    int *p;
public:
    Base(){
        p = new int;
        cout << "Base constructor allocating p" << endl;
    }
    ~Base(){
        delete p;
        cout << "Base destructor deleting p" << endl;
    }
};

int main(void){
    Base objA;
    /* Default copy constructor provided by compiler will copy objA.p to objB.p */
    /* Both will be pointing the same location */
    Base objB = objA;

    /* Both points to the same location */
    cout << "objA.p: 0x" << objA.p << endl;
    cout << "objB.p: 0x" << objB.p << endl;
    return 0;
}

output:






RUN TIME ERROR

Explaination:
  • Class contains the dynamic variable and its memory get allocated in constructor.
  • Assigning objA to objB, results in the bitwise copy of the variable and hence objB.p gets the same location that is being assigned to objA.p( objA.p and objB.p points to same location - as shown above in output )
  • Since objA and objB are local variable, hence it will be stored in stack. So when the main program is going to exit, it removes (pops) all local variables, it has stored in stack. Hence the destructor of the class is called for the objects.
  • May be destructor objA is called first which deletes the memory pointed by objA.p.
  • The memory pointed by objB.p is already deleted by destructor of objA, hence when objB calls its destructor during exit, it tries to delete the location which is already deleted and hence the program crashes and run time errors.
So inorder to overcome such problem Deep copying method is used. Copy constructor and assignment operator use deep copying technique.


Let write copy constructor and assignment operator to avoid this problem.


Copy constructor

Points:
  • It is constructor , it has the same name as class and does not have any return type.
  • Since it needs to copy an existing object, it needs to take Base class object as a parameter.
  • Copy constructor is a member function of Base class and parameter is also a object of Base class, hence it can directly access the private data of the parameter.
  • The parameter must be passed by reference, not by value. Passing by value again results in the creation of the temporary Base class object which will call copy constructor again. This will result in infinite recursion until stack memory runs out.

Without copy constructor.

#include <iostream>
using namespace std;

class Base{
public:
    int *p;
public:
    Base(){
        p = new int;
        *p = 10;
        cout << "Base constructor allocating p" << endl;
    }
    ~Base(){
        delete p;
        cout << "Base destructor deleting p" << endl;
    }
};

int main(void){
    Base objA;
    Base objB = objA;
    cout << "objA.p: 0x" << objA.p << " *objA.p: " << *objA.p << endl;
    cout << "objB.p: 0x" << objB.p << " *objB.p: " << *objB.p << endl;
    return 0;
}

output:







Note: 
    Both class object points to same memory location.
    After printing "Base destructor deleting p", program crashes. 

With copy constructor

#include <iostream>
using namespace std;

class Base{
public:
    int *p;
public:
    Base(){
        p = new int;
        *p = 10;
        cout << "Base constructor allocating p" << endl;
    }
    ~Base(){
        delete p;
        cout << "Base destructor deleting p" << endl;
    }

    /* Copy constructure */
    Base ( const Base &obj){
        p = new int;
        *p = *obj.p;
        cout << "Copy constructor called" << endl;
    }
};

int main(void){
    Base objA;
    Base objB = objA;
    cout << "objA.p: 0x" << objA.p << " *objA.p: " << *objA.p << endl;
    cout << "objB.p: 0x" << objB.p << " *objB.p: " << *objB.p << endl;
    return 0;
}

output:

Note: 
Copy constructor provides a different pointer and copies the value in the new pointer.
Const is used in the copy constructor. The reason it should receive a const parameter is that you're not changing the object you're copying from.

Overloaded Assignment Operator

First point to note that it is possible in C++ to do a self-assignment.
So it is a good idea to do a check for self-assignment at the top of an overloaded assignment operator.
It almost have identical code that is present in the copy constructor.

with overloaded assignment operator.

#include <iostream>
using namespace std;

class Base{
public:
    int *p;
public:
    Base(){
        p = new int;
        *p = 10;
        cout << "Base constructor allocating p" << endl;
    }
    ~Base(){
        delete p;
        cout << "Base destructor deleting p" << endl;
    }

    /* Copy constructure */
    Base ( const Base &obj){
        p = new int;
        *p = *obj.p;
        cout << "Copy constructor called" << endl;
    }
  
    /* Overloaded assignment operator */
    Base& operator= (const Base &obj){
        cout << "Assignment operator constructor called" << endl;
        /* Self assignment check */
        /* if both the address is same do nothing return */
        if( this == &obj)
            return *this;
        /* Code same as copy constructor */
        p = new int;
        *p = *obj.p;
        /* return the existing object */
        return *this;
    }
};

int main(void){
    Base objA;
    Base objB;
    objB = objA;
    cout << "objA.p: 0x" << objA.p << " *objA.p: " << *objA.p << endl;
    cout << "objB.p: 0x" << objB.p << " *objB.p: " << *objB.p << endl;
    return 0;
}
ouput:
Both the objects have pointers pointing to different location.

2 comments: