10 Constructor & Destructor
Jyotika Doshi
In the last module we have covered the following topics:
- Constructor
- Compiler defined Default Constructor
- User defined Default Constructor
- Parameterized Constructor
- Constructor Overloading
- Destructors
Introduction
We have seen how we can create a parameterized constructor. A parameterized constructor is one which has one or more arguments of any type. But there is one exception to this-
A single parameterized constructor can have parameter of any type except the object of the same class.
for example we cannot create the following Constructor in the Time class
Time(Time t)
{
// code
}
We may need this constructor if we want to create copy of an object . For example:
Time t1 (10,30,6) ;
Time t2 (t1); // creating the copy of object t1
Here, to construct object t2 we need the above constructor. But compiler will not allow us to create this constructor.
The reason is very logical: Objects are passed by value in C++ , thus when an object is passed by value , a temporary copy of object is created in the memory. Here, Time object is passed as parameter to constructor thus a temporary Time object is required to be created , to create this temporary Time object the same constructor with Time object as parameter would be invoked and this process would run infinitely. Thus to avoid this situation compiler does not allow to pass a Time object in constructor as parameter.
If passing the object of the same class is not possible in the constructor as a parameter then how to create copy of an object?
The answer is – Copy Constructor
Copy Constructor:
A copy constructor is a constructor which has the object reference as a parameter i.e. instead of passing object by value we pass reference of the object. The syntax of copy constructor is:
ClassName ( const ClassName & )
Here the parameter is reference of object and not the copy of object, therefore the object is passed by address.
(Note: Here the object reference should be passed as const so that the original object is not accidently modified in the copy constructor. )
Just like constructor and destructor C++ compiler also provides a copy constructor . Whenever copy of an object is created , this copy constructor is invoked . Compiler provided copy constructor creates a shallow copy of the object i.e. it copies the object bit by bit.
The below statement will invoke compiler provided copy constructor for creating object t2 which creates the exact copy of object t1 :
Time t2(t1) ;
Copy constructor is always invoked in the following cases:
- An object is initialized with the other object of the same class. For example:
Time t1(12,30,30);
Time t2 = t1 ;
Time t3(t1) ;
Here both object t2 and t3 are created using copy constructor
Note: The statement Time t3(t1) is same as Time t3=t1
Here the object t1 is created by invoking a usual parameterized constructor having 3 arguments.
But the object t2 and t3 are initialized with object t1 , so here copy constructor is invoked.
Note that if we first create an object and then assign another object as:
Time t3 ;
t3= t2 ;
Then object t3 is created using default constructor and not the copy constructor.
- When an object is passed as parameter to a function and when a function returns an object. In both the cases compiler needs to create a temporary object and this temporary object is created using copy constructor.
Example:
voiddisplayTime(Time t) ; // The parameter is a Time object
Time calculateTime(int milliseconds ) ; // Return type is Time object
User Defined Copy Constructor :
User can also define a copy constructor . If user defines a copy constructor then compiler will not provide any copy constructor.
Example:
For Time class the user defined copy constructor can be written as:
class Time{
// data members and methods
Time (const Time & t)
{
seconds = t.seconds ;
minutes = t.minutes ;
hours = t.hours ;
}
};
Need for User Defined Copy Constructor :
If bit by bit copy i.e. field by field copy is required then there is no need to define a user defined copy constructor as the compiler defined copy constructor does the same job.
In the previous example the Time class actually does not require the user defined copy constructor as the compiler defined copy constructor does the same thing i.e. field by field copy.
User defined copy constructor is needed when a deep copy is required. Usually it is required if there is dynamic memory allocation in constructor. Consider the following example:
class Vector
{
int size;
int *arr ;
public:
Vector()
{
//Default Constructor
size=0;
arr=null
}
Vector(int s)
{
size=s;
arr=new int[size] ;
for(int i=0 ; i<size ;i++)
{
arr[i] = 0 ;
}
};
~Vector()
{
cout<<”destructor invoked ” ;
delete [ ] arr ;
}
};
void main()
{
Vector v1(5);
Vector v2=v1;
}
Here object v2 is created using compiler provided copy constructor therefore the data members of object v2 will be the exact copy of data members of object v1. Thus the pointer variable *arr will also be copied as it is. The address pointed by v1.arr is copied to v2.arr and no separate dynamic memory is allocated to the pointer v2.arr
Thus v1.arr and v2.arr both will point to the same memory area.
Lets understand this point with the help of a diagram:
Here both object are sharing the same address in arr which can creates problem in the following cases:
- When one object modify this arr pointer, the changes are reflected in the other object also.
Suppose if we write the following statements:
v1.arr[0] = 10 ;
cout<<v1.arr[0] << “\n”;
cout<<v2.arr[0] ;
output: 10
10
This is happening because both v1.arr and v2.arr are sharing the same memory and no separate dynamic memory is allocated for v2.arr.
- Here if one of the object goes out of scope then the user defined destructor is invoked which will deallocate the dynamic memory while the other object is still pointing to the same memory which is being deallocated by compiler.
consider the following example:
void main()
{
Time t1(10,20,30) ;
{
Time t2(t1) ;
- // some code
} // object t2 goes out of scope
}
In the code above when object t2 goes out of scope , dynamic memory is deallocated by destructor. But object t1 is still pointing to same memory. See the diagram:
To avoid this kind of behavior, user defined copy constructor is needed. Let’s see how to define a copy constructor in the above example:
class Vector
{
int size;
int *arr ;
public:
//Default Constructor
Vector()
{
size=0;
arr=null
}
Vector(int s)
{
size=s;
arr=new int[size] ;
}
//copy constructor
Vector(const Vector &v)
{
size= v.size;
arr= new int[v.size] ; // allocate seperate memory
for(int i=0 ; i<v.size ;i++)
{
arr[i] = v.arr[i] ;
}
}
~Vector()
{
cout<<”destructor invoked ” ;
delete [ ] arr ;
}
} ;
Now if we create objects :
Vector v1 (5) ;
Vector v2=v1;
Here for creating object v2 , user defined copy constructor is invoked which allocate separate memory to v2.arr .
Destructors
In C++ objects are destroyed using Destructor function. Destructors are the counterpart of constructor. Destructor function is automatically invoked by compiler when the object goes out of scope . Just like constructor , destructor function have the same name as the class, preceded by tild (~) .
Syntax:
~classname()
{
// Definition of Destructor function
}
So the Destructor function for Time class can be written as:
~Time()
{
cout<<“object destroyed” ;
}
The code written in the body of the above destructor is executed when the object of Time goes out of scope.
Objects are destroyed in the reverse order of their construction. The reason is local objects are stored in stack , so the object which is defined last , destroyed first.
There are certain rules for destructor function:
- – Does not have a return type.
- – Does not accept any arguments, therefore a class can have at most one destructor ; destructor overloading is not possible.
If we do not specify any destructor , compiler will generate a destructor that does nothing. Compiler will invoke the destructor in the following cases:- An object goes out of scope
- – A dynamically allocated object is explicitly deleted using the delete keyword
Note: Although constructors and destructors are invoked automatically , calling them explicitly is also possible.
Let’s see the complete example which keep track of the number of Time objects :
class Time
{
private:
int seconds;
int minutes;
int hours;
static int count ;
public:
Time() {
count++ ;
cout<< “no. of objects “<< count ;
}
// Destructor for Time class
~Time() {
cout<< “ object destroyed ” ;
count- – ;
cout<< “no. of objects “<< count ;
}
};
void main()
{
Time t1 ;
Time t2 ;
} // object t1 and t2 goes of scope here so destructor is invoked
In the above example the static count variable keeps track of number of Time objects . The value of this variable is incremented when the object is created ( in the constructor) and when the object is destroyed the value is decremented (in the destructor ) .
In most of the cases we donot require to write destructor function , the default destructor is good enough. It is useful for the classes which require to:
– Write clean up code , for example – closing a file , releasing some resources occupied by the object.
-Deallocate the dynamically allocated memory .
Example: Destructor and Dynamic memory Allocation
Let’s see the example of a class where user defined destructor function is required .
class Vector
{
int size;
int *arr ;
public:
Vector()
{
size=0;
arr=null
}
Vector(int s)
{
size=s;
arr=new int[size] ;
for(int i=0 ; i<size ;i++)
{
arr[i] = 0 ;
}
}
void display()
{
for(int i=0;i<size;i++)
{
cout<<arr[i] ;
}
}
//Destructor
~Vector()
{
cout<<”destructor invoked ” ;
delete [ ] arr ;
}
};
voidfun()
{
Vector v(5) ; // calls the constructor and allocates dynamic memory in variable “arr” v.display();
} // the object v goes out of scope , destructor invoked
In the above example the object “v” goes out of scope at the end of function fun() . The destructor function ~Vector() is invoked which deletes the dynamic memory allocated to “arr” . In above example destructor function is required to free the dynamically allocated memory. In absence of the destructor the memory occupied by the object will be free when the function ends but thedynamic memory allocated to pointer variable *arrwill remain occupied . Let’s understand this with the help of diagram:
Without the user defined destructor function :
The statement in function fun()
Vector v(5) ; will allocate the memory :
dynamically allocated | ||||||
memory | ||||||
Object v | 0 | 0 | 0 | 0 | 0 | |
size=5 | ||||||
*arr = |
When function ends then the object is destroyed using compiler provided destructor which destroy the object and release the memory occupied by the object, but it will not release the dynamic memory :
When function ends then the object is destroyed using compiler provided destructor which destroy the object and release the memory occupied by the object, but it will not release the dynamic memory :
Summary:
- In C++ a copy of an object is created using copy constructor .
- In case a class does not define any copy constructor, compiler provides one.
- Copy constructor does a shallow copy of object.
- A class must define a copy constructor if it involves dynamic memory allocation ( for deep copy of object).
- At the time of object destruction the Destructor function is invoked.
- Just like constructor, compiler will also provide a default destructor in case a class does not define any destructor.
- Destructor are invoked implicitly by complier at the time of object creation and destruction respectively.
- Destructor overloading is not possible.
- A class must contain a Destructor function if it involves dynamic memory allocation.
you can view video on Constructor & Destructor |
Additional References
1) Stanley Lippmann, “C++ Primer”, Pearson Education.
2) Bjarne Stroustrup, “The C++ Programming Language”, Pearson Education.
3) Scott Mayer, “Effective C++”, Addison Wesley.
4) Bhushan Trivedi , Programming with ANSI C++, 2/e , Oxford University Press.
5) Yashavant P. Kanetkar “Let Us C++” , Bpb Publications.
6) Abhiram G. Ranade, “An Introduction to Programming through C++” , McGraw Hill
7) Ellis and B. Stroustrup “Annotated C++ Reference Manual”, http://www.stroustrup.com/arm.html
8) Herbert Schildt, “Complete Reference C++”, McGraw Hill Publications.
9) Ashok Kamthane, “Object Oriented Programming with ANSI and Turbo C++”, Pearson Education
10) E Balaguruswami, “Object Oriented Programming With C++”, Tata McGraw Hill
“C++ FAQs”, Pearson Education