This article provides a comprehensive overview of constructors and destructors in C++ programming language along with example programs.
Contents
Introduction
Until now in our C++ programs, we are initializing the data members of a class by creating one or more member functions and passing values as parameters to those functions. We know that C++ considers user-defined types on par with pre-defined types. We can initialize a pre-defined type at the time of declaration itself as shown below:
int x = 20;
Similarly, to initialize the data members when an object is created, C++ provides a special member function called a constructor and to de-initialize an object, another special member function called a destructor is used.
Constructor
Constructor is a special member function which is invoked automatically when an object is created to initialize the data members of that object. Following are the characteristics of a constructor:
- The name of a constructor is same as the class name.
- A constructor must be declared in public section of the class.
- It should not be called explicitly as it is automatically called when an object is created.
- A constructor doesn’t return any value. So, a constructor doesn’t have any return type. Not even void.
- A constructor cannot be inherited or virtual.
- The address of a constructor cannot be referred in programs. So, pointers and references cannot be used with constructors.
- A constructor cannot be declared as static, volatile, or const.
- Constructors can be overloaded.
- Constructors can have default arguments.
Like a member function, we can define a constructor inside or outside the class. Syntax for defining a constructor inside the class is as follows:
class ClassName
{
private:
...
public:
...
ClassName( )
{
//Body of constructor
}
...
};
Syntax for defining a constructor outside the class is as follows:
class ClassName
{
private:
...
public:
...
ClassName( ); //Constructor declaration
...
};
ClassName::ClassName( ) //Constructor definition
{
//Body of constructor
}
Following is an example which demonstrates constructor defined inside the class:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student() //Constructor
{
age = 20;
branch = "CSE";
}
void get_details()
{
cout<<"Age of student is: "<<age<<endl;
cout<<"Branch of student is: "<<branch<<endl;
}
};
int main()
{
Student s1;
s1.get_details();
return 0;
}
Output for the above program is as follows:
Age of student is: 20
Branch of student is: CSE
Following is an example which demonstrates constructor defined outside the class:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student(); //Constructor declaration
void get_details()
{
cout<<"Age of student is: "<<age<<endl;
cout<<"Branch of student is: "<<branch<<endl;
}
};
Student::Student() //Constructor definition
{
age = 20;
branch = "CSE";
}
int main()
{
Student s1;
s1.get_details();
return 0;
}
Output for the above program is as follows:
Age of student is: 20
Branch of student is: CSE
Types of Constructors
A constructor can be classified into following types:
- Dummy constructor
- Default constructor
- Parameterized constructor
- Copy constructor
- Dynamic constructor
Dummy Constructor
When no constructors are declared explicitly in a C++ program, a dummy constructor is invoked which does nothing. So, data members of a class are not initialized and contains garbage values. Following program demonstrates a dummy constructor:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
void get_details()
{
cout<<"Age of student is: "<<age<<endl;
cout<<"Branch of student is: "<<branch<<endl;
}
};
int main()
{
Student s1;
s1.get_details();
return 0;
}
Output for the above program is as follows:
Age of student is: 4883904
Branch of student is:
You can see that since age and branch are not initialized, they are displaying garbage values.
Default Constructor
A constructor which does not contain any parameters is called a default constructor or also called as no parameter constructor. It is used to initialize the data members to some default values. Following program demonstrates a default constructor:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student() //Constructor
{
age = 20;
branch = "CSE";
}
void get_details()
{
cout<<"Age of student is: "<<age<<endl;
cout<<"Branch of student is: "<<branch<<endl;
}
};
int main()
{
Student s1;
s1.get_details();
return 0;
}
Output for the above program is as follows:
Age of student is: 20
Branch of student is: CSE
Parameterized Constructor
A constructor which accepts one or more parameters is called a parameterized constructor. Following program demonstrates a parameterized constructor:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student(int a, string b) //Constructor
{
age = a;
branch = b;
}
void get_details()
{
cout<<"Age of student is: "<<age<<endl;
cout<<"Branch of student is: "<<branch<<endl;
}
};
int main()
{
Student s1(21, "CSE");
s1.get_details();
return 0;
}
Output for the above program is as follows:
Age of student is: 21
Branch of student is: CSE
Copy Constructor
A constructor which accepts an already existing object through reference to copy the data member values is called a copy constructor. As the name implies a copy constructor is used to create a copy of an already existing object. Following program demonstrates a copy constructor:
#include <iostream>
using namespace std;
class Square
{
private:
int side;
public:
Square(int s)
{
side = s;
}
Square(Square &s)
{
side = s.side;
}
void display()
{
cout<<"Side of square is: "<<side<<endl;
}
};
int main()
{
Square s1(10);
Square s2(s1);
Square s3 = s2;
s1.display();
s2.display();
s3.display();
return 0;
}
Output for the above program is as follows:
Side of square is: 10
Side of square is: 10
Side of square is: 10
In the above program you can see that there are two ways for calling a copy constructor. One way is to call the copy constructor by passing the object as a parameter (in case of s2). Other way is to directly assign an existing object to a new object (in case of s3).
Note: Copying an object through assignment only works during object declaration. After declaring an object, assignment just serves its basic purpose.
Dynamic Constructor
A constructor in which the memory for data members is allocated dynamically is called a dynamic constructor. Following program demonstrates dynamic constructor:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
int *marks;
public:
Student(int n) //Constructor
{
marks = new int[n];
cout<<"Enter marks for "<<n<<" subjects: ";
for(int i=0; i<n; i++)
cin>>marks[i];
}
void display_marks(int n)
{
cout<<"Marks are: ";
for(int i=0; i<n; i++)
cout<<marks[i]<<" ";
}
};
int main()
{
int n;
cout<<"Enter no. of subjects: ";
cin>>n;
Student s1(n);
s1.display_marks(n);
return 0;
}
Input and output for the above program are as follows:
Enter no. of subjects: 4
Enter marks for 4 subjects: 20 18 19 16
Marks are: 20 18 19 16
Constructor with Default Arguments
A constructor like a member function can have default arguments. The default arguments should be declared at the end of the parameters list. Following program demonstrates a constructor with default arguments:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
int *marks;
public:
Student(string branch, int n = 4) //Constructor
{
marks = new int[n];
cout<<"Enter marks for "<<n<<" subjects: ";
for(int i=0; i<n; i++)
cin>>marks[i];
cout<<"Student marks are: ";
for(int i=0; i<n; i++)
cout<<marks[i]<<" ";
cout<<endl;
}
};
int main()
{
int n;
cout<<"Enter no. of subjects: ";
cin>>n;
Student s1("CSE", n);
Student s2("CSE");
return 0;
}
Output for the above program is as follows:
Enter no. of subjects: 3
Enter marks for 3 subjects: 10 20 30
Student marks are: 10 20 30
Enter marks for 4 subjects: 15 18 16 20
Student marks are: 15 18 16 20
Constructor Overloading
Declaring multiple constructors with varying number of arguments or types of arguments is known as constructor overloading. All the overloaded constructors will have the same name as that of the class. Following program demonstrates constructor overloading:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student()
{
cout<<"Default student constructor is invoked"<<endl;
}
Student(int age)
{
cout<<"Student constructor with one parameter is invoked"<<endl;
}
Student(string n, string reg, int a, string br)
{
cout<<"Student constructor with four parameters is invoked"<<endl;
}
};
int main()
{
Student s1;
Student s2(20);
Student s3("Mahesh", "501", 20, "CSE");
return 0;
}
Output for the above program is as follows:
Default student constructor is invoked
Student constructor with one parameter is invoked
Student constructor with four parameters is invoked
Destructor
A destructor is a special member function that is invoked automatically when the object goes out of scope and is used to perform clean up actions. Following are the characteristics of a destructor:
- Name of the destructor is same as the class name. However, destructor name is preceded by the tilde (~) symbol.
- A destructor is called when a object goes out of scope.
- A destructor is also called when the programmer calls delete operator on an object.
- Like a constructor, destructor is also declared in the public section of a class.
- The order of invoking a destructor is the reverse of invoking a constructor.
- Destructors do not take any arguments and hence cannot be overloaded.
- A destructor does not return a value.
- The address of a destructor cannot be accessed in a program.
- An object with a constructor or a destructor cannot be used as members of a union.
- Destructor cannot be inherited.
- Unlike constructors, destructors can be virtual.
Syntax for creating a destructor is as shown below:
~ClassName( )
{
//Body of destructor
}
Following program demonstrates a destructor:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student(string n)
{
regdno = n;
cout<<"Memory allocated for student "<<regdno<<endl;
}
~Student()
{
cout<<"Memory deallocated for student "<<regdno<<endl;
}
};
int main()
{
Student s1("501");
Student s2("502");
Student s3("503");
return 0;
}
Output for the above program is as follows:
Memory allocated for student 501
Memory allocated for student 502
Memory allocated for student 503
Memory deallocated for student 503
Memory deallocated for student 502
Memory deallocated for student 501
Note: If a destructor is not defined by the programmer, C++ compiler will create a inline public destructor automatically.
Constructors and Destructors
Explicit Invocation
Constructors and destructors can be invoked (called) explicitly from the main() function. A constructor can be invoked as shown below:
ClassName(params-list );
For example, constructor of Student class can be called as shown below:
Student( );
In case of a destructor, we need to write the class name along with the destructor name as follows:
object_name.ClassName::~ClassName( );
or
object_name.~ClassName( );
For example, destructor of Student class can be called as follows:
s1.~Student( );
A destructor can be called from a constructor as follows:
class Student
{
...
Student( )
{
this->Student::~Student( ); //Call to destructor
...
}
...
};
Similarly, a constructor can be called from a destructor as follows:
class Student
{
...
~Student( )
{
Student( ); //Call to constructor
...
}
...
};
Private Constructor and Destructor
Sometimes programmers may want to prevent direct access to a constructor or destructor. In such cases, they can be made private. Access to a private constructor or destructor can be granted through public member functions or through a friend function.
Following program demonstrates creating private constructors and a destructor and accessing them using public member functions:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
Student()
{
}
Student(string n)
{
regdno = n;
cout<<"Memory allocated for student "<<regdno<<endl;
}
~Student()
{
cout<<"Memory deallocated for student "<<regdno<<endl;
}
public:
Student* init(string reg)
{
return new Student(reg);
}
void destroy(Student *s)
{
delete(s);
}
};
int main()
{
Student *s;
s = s->init("501");
s->destroy(s);
return 0;
}
Output for the above program is as follows:
Memory allocated for student 501
Memory deallocated for student 501
Object Copy
Frequently while programming there will be need to create a copy of an existing object. This can be done in three ways:
Shallow Copy
In this method the copy (object) only copies the addresses (for dynamic memory) of the source object’s members. This is similar with the semantics of static data members. A same copy is shared among several objects. Similarly, in shallow copy method, both the source and copied objects share the same memory location.
Deep Copy
In this method the copied object maintains a copy of the value of each source object’s data members. To achieve this, programmer should explicitly provide a copy constructor to copy all the values of source object’s members (including dynamic memory).
Lazy Copy
This method has the advantages of the above two methods. Initially during copying, it uses shallow copying and maintains a counter to keep track of how many objects are sharing the data. When there is an attempt to modify the data, it may initiate deep copy, if necessary.
Constant Objects
When there is a need to create a read-only object, the object can be declared as a constant object using the const keyword. Syntax for creating a constant object is as follows:
ClassName const object_name(params-list);
Following are the characteristics of a constant object:
- Constant object can be initialized only through a constructor.
- Data members of a constant object cannot be modified by any member function.
- Constant object are read-only objects.
- Constant objects should be accessed only through constant member functions.
Following program demonstrates a constant object:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student(string br)
{
branch = br;
}
void get_branch() const
{
cout<<"Branch is: "<<branch;
}
};
int main()
{
Student const s("CSE");
s.get_branch();
return 0;
}
Output for the above program is as follows:
Branch is: CSE
Anonymous Objects
An object which has no name is known as an anonymous object. Syntax for creating an anonymous object is as follows:
ClassName(params-list);
An anonymous object can be created and used in any statement without using any name. It is automatically destroyed after the control moves on to the next statement. Following program demonstrates an anonymous object:
#include <iostream>
using namespace std;
class Student
{
private:
string name;
string regdno;
int age;
string branch;
public:
Student(string reg)
{
regdno = reg;
cout<<"Student with regdno "<<regdno<<" is created";
}
};
int main()
{
Student("501"); //Anonymous object
return 0;
}
Output for the above program is as follows:
Student with regdno 501 is created
Following are the advantages of anonymous objects:
- They result in cleaner, shorter, and efficient code.
- There is no need to name objects which are going to be used temporarily.
- No need to declare them.
- Unnamed are objects are destroyed automatically after their use is over.
Anonymous Classes
A class which does not have a name is called an anonymous class. They are generally created when the class body is very short and only one object of that class is required. Following are the restrictions on anonymous classes:
- Anonymous classes cannot have a constructor.
- Anonymous classes can have a destructor.
- Anonymous classes cannot be passed as arguments to functions.
- Anonymous classes cannot be used as return values from functions.
Following program demonstrates an anonymous class:
#include <iostream>
using namespace std;
typedef class //Anonymous class
{
private:
string name;
string regdno;
int age;
string branch;
public:
void set_branch(string br)
{
branch = br;
}
void get_branch()
{
cout<<"Branch is: "<<branch;
}
}Student;
int main()
{
Student s;
s.set_branch("CSE");
s.get_branch();
return 0;
}
Output for the above program is as follows:
Branch is: CSE
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
Leave a Reply