Lecture No.09              
Review
Copy Constructor
Copy constructors are used when:

Initializing an object at the time of creation (we want to create an object with state of a pre existing object)
When an object is passed by value to a function (As you know temporary copy of object is created on stack so we need copy constructor to create that temporary object with the state of actual object being passed).


Example

 

void func1(Student student){
}

int main(){

            Student studentA;
            Student studentB = studentA;

            func1(studentA);

}







Copy Constructor (Syntax)

Student::Student( const Student &obj){
           
     /*copying values to newly created object*/
    rollNo = obj.rollNo;
            name = obj.name;
            GPA = obj.GPA;
}


As was the case with default constructor compiler also generates copy constructor by itself however we can override that copy constructor by writing our own copy constructor as shown in example below,
As was the case with default constructor compiler also generates copy constructor by itself however we can override that copy constructor by writing our own copy constructor as shown in example below,

#include <iostream>
using namespace std;

class Student{

            int rollNo;
public:
            Student(){
                        rollNo = 0;
                        cout<<”I am default constructor of Student class…\n”;
            }

Student(const Student &obj){
             cout<<”I am copy constructor of Student class\n”;

   rollNo = obj.rollNo;
             
}
};

int main()
{
            Student aStudent;
            /*default constructor is implicitly called at this point*/

            Student bStudent = aStudent;
            /*copy constructor is implicitly called at this point*/

system(“pause”);
return 0;
}




When we initialize one object with another then the compiler copies state of one object to the other using copy constructor by assigning data member values of previous object to newly created object. This kind of copying is called shallow copying.

Shallow copy using default Copy Constructor (Syntax)

Student::Student(      const Student & obj ){

            rollNo = obj.rollNo;
            name = obj.name;
            GPA = obj.GPA;
}


This kind of copying is called shallow copying

Example

Student studentA;
Student studentB = studentA;  /*Shallow copy: compiler will use copy constructor to assign studentA values to newly created object studentB*/

Example
Student studentA(“Ahmad”);


Student studentB = studentA;


#include <iostream>
using namespace std;

class Student{
      char * name;
            int rollNo;
public:
            Student(char * aName, int arollNo){
        name = new char[strlen(aName)+1];
        strcpy(name,aName);
                        rollNo = arollNo;
    
            }
    Student(const Student &obj){
       name = obj.name;
       rollNo = obj.rollNo;
    
             
    }
    void showName(){
         cout<<name<<endl;
        
         }
    ~Student(){
    
               delete []name;
              
               }
};

int main()
{
            Student studentA("AHMAD",1);
            Student studentB = studentA;
            /*copy constructor is implicitly called at this point*/
   
            studentA.showName();
    studentB.showName();

    system("pause");
    return 0;
}

Shallow copy works fine if our class doesn’t include dynamic memory allocation but in case of dynamic memory allocation it leads to dangling pointer problem as explained below.

Problem is Shallow Copy

Student class data member name of char * type is added to store the name of student and it is using dynamic memory according to the length of name entered by user for student.

Student class data member name (char *) of  object studentB is also pointing to memory allocated for datamember name of object studentA, due to this there may be two kinds of problems.

Suppose we delete first object studentA for some reason then its destructor will also free memory allocated by it hence memory area containing name “AHMAD” will also be freed and will be given to some other application by operating system, but studentB member name is still pointing to that area so issue of “Dangling Pointer” [Pointer pointing to incorrect memory location] will arose.
Same will happen if object studentB is deleted then studentA object data member name will become dangling pointer. This has been explained below,
Let us change code in main to make our second object studentB in a new local scope as shown below,

int main(){
Student studentA(“Ahmad”,1);

{
     Student studentB = studentA;
}
}


Now if we will try to print the name of object studentA our program will not show any output as name is pointing to some irrelevant memory address,


Complete program code is given below,

#include <iostream>
using namespace std;

class Student{
      char * name;
            int rollNo;
public:
            Student(char * aName, int arollNo){
        name = new char[strlen(aName)+1];
        strcpy(name,aName);
                        rollNo = arollNo;
    
            }
    Student(const Student &obj){
       name = obj.name;
       rollNo = obj.rollNo;
             
    }
    void showName(){
         cout<<name<<endl;
        
         }
    ~Student(){
               delete []name;
               }
};

int main()
{
            Student studentA("AHMAD",1);
           
    {
            Student studentB = studentA;
            /*copy constructor is implicitly called at this point*/
    }

            studentA.showName();

    system("pause");
    return 0;
}



Secondly if for some reason we change name of object studentA the value of object studentB will also be changed as it pointing to same memory location.

#include <iostream>
using namespace std;

class Student{
      char * name;
            int rollNo;
public:
            Student(char * aName, int arollNo){
        name = new char[strlen(aName)+1];
        strcpy(name,aName);
                        rollNo = arollNo;
    
            }
    Student(const Student &obj){
       name = obj.name;
       rollNo = obj.rollNo;
             
    }
   
    void setName(char * aName){
         strcpy(name,aName);
        
         }
  
    void showName(){
         cout<<name<<endl;
        
         }
    ~Student(){
    
               delete []name;
              
               }
};

int main()
{
            Student studentA("AHMAD",1);
            Student studentB = studentA;
            /*copy constructor is implicitly called at this point*/
       
            studentA.showName();
            studentB.showName();

   
            studentA.setName("MOEEN");
            studentA.showName();
            studentB.showName();

    system("pause");
    return 0;
}

We resolve these two issues using deep copy.


We write deep copy code in copy constructor so that when we create new object from an existing object using copy constructor we also allocate new dynamic memory for data members involving dynamic memory as shown below,

Student::Student( const Student & obj){

            int len = strlen(obj.name);
            name = new char[len+1]; // assignming new
/*dynamic memory to data member name of char * type for newly created object.*/
            strcpy(name, obj.name);
           
            //copy rest of the data members in the same way
}

            Now we see what happens when we created objects in main as shown below,


int main(){
Student studentA(“Ahmad”,1);

{
     Student studentB = studentA;
}
}





Now when we will execute code with object studentB in local scope our code still works fine and shows name for object studentA as now deletion of object studentB has no effect on object studentA as shown below,


Example

#include <iostream>
using namespace std;

class Student{
    char * name;
            int rollNo;
public:
            Student(char * aName, int arollNo){
        name = new char[strlen(aName)+1];
        strcpy(name,aName);
                        rollNo = arollNo;
    
            }
    Student(const Student &obj){
      
       name = new char[strlen(obj.name)+1];
       strcpy(name,obj.name);
       rollNo = obj.rollNo;
    
             
    }
   
   void showName(){
         cout<<name<<endl;
        
         }
    ~Student(){
    
               delete []name;
              
               }
};

int main()
{
            Student studentA("AHMAD",1);
    {
            Student studentB = studentA;
            /*copy constructor is implicitly called at this point*/
    }
            studentA.showName();
     
    system("pause");
    return 0;
}


  1. In case our class doesn’t involve dynamic memory then default copy constructor that performs shallow copy works fine.
  2. In case our class has any data member involving dynamic memory we have to write our own code in copy constructor to perform deep copy.
  3. Copy constructor is normally used to perform deep copy
  4. If we do not make a copy constructor then the compiler performs shallow copy
  5. Shallow copy performs bitwise copy.


  1. Destructor is used to free memory that is allocated through dynamic allocation. We have to free memory allocated using new operator by over self in destructor otherwise it remain occupied even after our program ends.
  2. Destructor is used to perform house keeping operations.
  3. Destructor is a function with the same name as that of class, but preceded with a tilde ‘~’

Example

class Student
{
           
public:
            ~Student(){
                        if(name){
                                    delete []name;
                        }
            }
}

Overloading
Destructors cannot be overloaded.

Sequence of Calls
Constructors and destructors are called automatically
Constructors are called in the sequence in which object is declared
Destructors are called in reverse order

Example
Student::Student(char * aName){
           
                        cout << aName << “Constructor\n”;
            }
            Student::~Student(){
                        cout << name << “Destructor\n”;
            }
};
Example
int main()
{
            Student studentB(“Ali”);
            Student studentA(“Ahmad”);
            return 0;
}

Example

Output:
Ali Constructor
Ahmad Constructor
Ahmad Destructor
Ali Destructor


In accordance to principle of information hiding data members of a class are declared as private so that outside world can not access the private data of the object only an interface is provided to outside world in the form of functions.
Accessor functions are also used to access private data of the object, we provide accessor functions to get and set private data members of the class.
We also add error checking code in accessor functions to reduce errors so that object doesn’t move in illegal state.

Example – Accessing Data Member

Example - Setter

class Student{
           
            int rollNo;
public:
            void setRollNo(int aRollNo){
                        rollNo = aRollNo;
            }
};

Avoiding Error
void Student::setRollNo(int aRollNo){
            if(aRollNo < 0){
              rollNo = 0;
            }
            else
            {
              rollNo = aRollNo;
            }
}
Example - Getter

class Student{
           
            int rollNo;
public:           
            int getRollNo(){
                        return rollNo;
            }
};

Good Practice:
Never return a handle to a data member from getter function because you are never sure that function accessing the reference will not change the value of the variable.

Consider the code of a general class given below,

class Student{
            int rollNo;
            char *name;
            float GPA;
public:
            int getRollNo();
            void setRollNo(int aRollNo);
};

The compiler reserves space for the functions defined in the class





·         Space for data is not allocated (since no object is yet created)


this Pointer
Student s1, s2, s3;



this Pointer

·         Function space is common for every variable
·         Whenever a new object is created:
o   Memory is reserved for variables only
o   Previously defined functions are used over and over again


Memory layout for objects created:



this Pointer

·         Address of each object is passed to the calling function.
·         This address is de-referenced by the functions and hence they act on correct objects



Passing this Pointer

·         Whenever a function is called the this pointer is passed as a parameter to that function.
·         Function with n parameters is actually called with n+1 parameters

Example

void Student::setName(char *)

is internally represented as,

void Student::setName(char *, const Student *)

Declaration of this

DataType * const this;

Compiler Generated Code

Post a Comment

Don't Forget To Join My FB Group VU Vicky
THANK YOU :)

Previous Post Next Post