Constructor Final 10-10-2024
Constructor Final 10-10-2024
What is a Constructor?
Constructor is a specially defined method or
member function (or special method or special member function) of a class
which are used to assign (initialize) values to the object’s members of the
class objects. These functions are automatically executed (or called), when
an instance (object) of class is created. (Constructor is invoked automatically
at the time an object of a class is created).
Characteristics of Constructors:
The name of the constructor is the same as its class name to which it
belongs.
Constructors are usually declared in the public section (scope). However,
constructor can be declared in private section, as well.
Constructors are special member function of the class. So, do not return
values; hence they do not have a return type, not even void.
Constructors are invoked (gets called) automatically when we create the
object of the class and can take arguments to initialize the object’s data
members.
Like other C++ functions, constructor can have any number of
parameters (default parameters also) and a class may have any number
of overloaded constructors.
Constructors may have any accessibility, public, private or protected.
They cannot be inherited, though a derived class can call the base class
constructor.
A constructor may not be static.
It is not possible to take the address of a constructor.
Member functions may be called. But Constructors are special member
function of the class. So, no need to call these functions.
Constructors show polymorphism in a class
Why do we need Constructors?
The main purpose of a constructor is to
construct an object and assign values to the object's members.
Syntax:
The prototype of the constructor looks like this:
<class-name> ()
{
...
}
Example:
Default constructor:
The default constructor is a unique constructor that
is created and called by the compiler automatically without any parameters,
when no other user-defined constructor is explicitly declared within a class. It
is called for the default initialization of class internal members.
Format of default constructor:
class class_name
{
.........
Access_specifier:
class_name()
{
} //default constructor
.........
};
Example: Demonstrating the Default Constructor
#include<iostream>
using namespace std;
class Rectangle
{
public:
float l,b;
Rectangle() //Constructor created
{
l=3;
b=6;
}
};
int main()
{
Rectangle rect;
cout <<"Area of Rectangle : "<<rect.l*rect.b;
}
Output:
Area of Rectangle: 18
In the above program, first object rect belonging to class Rectangle is
created and then the constructor that initializes its data members.
Example: Demonstrating how the default constructor is called by
the compiler
#include<iostream>
using namespace std;
class Rectangle
{
public: int l=3,b=3;
};
int main()
{
Rectangle rect;
cout<<"Area of Rectangle : "<<rect.l*rect.b;
}
Output: Area of Rectangle: 9
In the above program, Default Constructor is called automatically by
the compiler which initializes the object data members that is l & b to
default value (3,3). So, the Area of Rectangle is 9.
If constructor does not explicitly defined then the compiler will provide a
default constructor implicitly.
Another way to define default constructor:
#include <iostream>
using namespace std;
class Person
{
private:
string name;
int age;
public:
Person(); // Default constructor
// Member function to display person's information
void displayInfo()
{
cout << "Name: " << name << ", Age: " << age << endl;
}
};
// Constructor definitions (outside class)
Person::Person()
{
name = "Puttaswamy";
age = 40;
}
int main()
{
// Creating Person objects using constructors
Person person1;
// Displaying information about the persons
cout << "Person 1: ";
person1.displayInfo();
return 0;
}
Output: Person 1: Name: Puttaswamy, Age: 40
Explanation:
1. We define a class called Person, with two private data
members, name and age.
2. Then, we declare the default constructor inside the class without any
definition.
3. We also have a class member function displayInfo(), which uses cout to
print the object's information.
4. Next, we define the default constructor outside the class (as
mentioned in the comment) using the scope resolution operator(::).
5. This constructor initializes the data member name to 'Unknown' and age
to 0 for any new object of the Person class.
6. In the main() function, we create an instance of the Person class
called person1 default constructor.
7. We then call the displayInfo() method on person1 using the dot
operator. This prints the output to the console, as given above.
Parameterized Constructor:
A parameterized constructor is a particular kind of constructor that
accepts one or more parameters as arguments when creating an object (A
parameterized constructor is a constructor that accepts parameters to
initialize an object with specific values at the time of its creation).
It enables you to supply unique values when initializing an object's data
members (This constructor is passed with parameters, and the object holds
the value of these parameters). In other words, a parameterized constructor
allows you to set initial values for the member variables of an object.
Syntax:
class ClassName
{
Access_specifier:
ClassName(parameter_type parameter1, parameter_type parameter2, ...)
{
// Constructor body
}
};
Here,
The class keyword indicates that we are defining a class.
The className is the name of the class, as well as the parameterized
constructor in this case.
Access_specifier is a data visibility controller that can be public, private,
or protected.
The terms paramater1 and parameter_type represent the name of the
parameter and its type.
Example:
#include <iostream>
using namespace std;
class Rectangle
{
private:
int width;
int height;
public:
// Parameterized constructor declaration
Rectangle(int w, int h)
{
width = w;
height = h;
}
void displayDimensions()
{
cout << "Width: " << width << ", Height: " << height << endl;
}
};
int main()
{
Rectangle rect(10, 5);
rect.displayDimensions();
return 0;
}
Output: Width: 10, Height: 5
Explanation:
The example continues the one above with the Rectangle class, two private
data members, i.e., width and height, and the member
function displayDimensions().
1. But instead of a default constructor, we have a parameterized
constructor that takes two integer parameters (w for width and h for
height).
2. Inside the constructor, the width and height attributes of the object are
initialized using the provided parameters.
3. In the main() function, we create an instance of the Rectangle class
named rect by passing the values
4. In the main() function, we create an instance of the Rectangle class
named rect by passing values 10 and 5 to the constructor,
i.e., Rectangle rect(10, 5).
5. We then call the displayDimensions() member function on the rect
object to display the width and height of the rectangle.
Example: Demonstrating the Parameterized Constructor
#include<iostream>
using namespace std;
class Square
{
public:
float side;
Square(float s) //Parameterized Constructor
{
side = s;
}
};
int main()
{
Square sq(5);
cout <<"Area of Square : "<<sq.side*sq.side<<endl;
}
Output: Area of Square : 25
Copy Constructor:
The copy constructor is a special type of constructor
which creates an object by initializing it with an object of the same class,
which has been created previously. In other words, a copy constructor is a
special type of constructor that makes a copy of an existing object as an
argument while creating a new object. In simple words, a copy constructor
creates a new object which is exact copy of the existing constructor, hence
it is called as copy Constructor. This type of constructor is used to copy
values of data members of one object into another object.
Syntax:
class ClassName
{
Access_specifier:
class_name (const class_name &obj)
{
//body of constructor;
}
};
Here,
class is the keyword used for the class definition.
ClassName is the name of the class in which the copy constructor is
defined and also the name of the constructor.
Access_specifier controls the data visibility of the constructor, which
could be public, private, protected
ClassName &obj: is a parameter of the copy constructor in C++ that
accepts a constant reference to the objects of the same class.
Code Example:
#include <iostream>
using namespace std;
class Person
{
private: string name;
int age;
public:
Person(string n, int a) // Parameterized constructor
{
name = n;
age = a;
}
Person(const Person &source) // Copy constructor
{
name = source.name;
age = source.age;
}
void displayInfo()
{
Output: Name: Shivani, Age: 25
Explanation:
1. In this example, we first define a Person class with two private data
members, the name of the type string, and the age of the type int.
2. The class contains a parameterized constructor that accepts a string n
for the name and integer a for the age.
3. It also has a copy constructor that takes a reference to another Person
object, i.e., const Person &source, as a parameter.
4. We also define a member function displayInfo() inside the class to print
the name and age of the person using the cout stream.
5. In the main() function, we create an instance of Person class
named person1 using the parameterized constructor with the values
"Shivani" and 25.
6. We then create another instance of Person named person2 by copying
the contents of person1 using the copy constructor. This happens
when Person person2 = person1 is executed.
7. Next, we call the displayInfo() method on person2, which prints the
information of person2 on the console.
The output shows that the copy constructor in C++ has copied the attributes
and values of the data members for person1 object to person2 object.
Example: Program demonstrating the Copy Constructor
#include<iostream>
using namespace std;
class CopyConst
{
private: int a, b;
public:
CopyConst(int a1, int b1) //Parameterized constructor
{
a = a1;
b = b1;
}
CopyConst(const CopyConst &obj2) // Copy constructor
{
a = obj2.a;
b = obj2.b;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
};
int main()
{
CopyConst obj1(10, 20); // Normal constructor is called
cout<<"Normal Constructor"<<endl;
cout << "Value of a and b" <<"\n a is : "<< obj1.getA() <<"\n b is : "<<
obj1.getB()<<endl; //Access values assigned by constructors
cout<<"\n Copy Constructor";
CopyConst obj2 = obj1; // Copy constructor is called
cout << "\n Value of a and b" <<"\n a is : "<< obj2.getA() <<"\n b is :
"<< obj2.getB();
return 0;
}
Output:
Normal Constructor
Value of a and b
a is : 10
b is : 20
Copy Constructor
Value of a and b
a is : 10
b is : 20
Constructor For Array of Objects:
An array of objects is a collection of
objects of the same class type stored in a single array. When an array of
objects is created, the constructor for each object in the array is called to
initialize each element.
An array of objects is a collection of objects of the same class type stored in
a single array data structure. In other words, it's an array with each entry
being a class instance, i.e., an array of class objects.
The constructor of each object in an array of objects is called to
initialize each element of the array.
This allows you to store and manage multiple objects of the same class
in a structured manner.
Here is how a constructor in C++ work with an array of objects:
The first step is to declare an array of items. This only allocates
memory for the array it hasn't called the constructor.
The second step is constructor invocation, i.e., the constructor of each
object is automatically called as you create them in the array. This
occurs if you initialize the array's objects using the array declaration.
Syntax:
class ClassName
{
Access_specifier:
ClassName(data_type parameter1, data_type parameter2)
{
// body of the Constructor
}
};
int main() {
ClassName objectsArray[size] =
{
ClassName(param1_value, param2_value), // Object 1
ClassName(param1_value, param2_value), // Object 2
};
//Additional code, if any
return 0
};
Here,
The class keyword indicates that a class is being defined.
ClassName is the name of the class as well as the constructor.
The access_specifier is a data visibility controller that can be public,
private, or protected.
The term parameter1 specifies the name of the parameter,
and data_type specifies the type of parameter.
param1_value: It denotes the value of the parameter.
Code Example:
#include <iostream>
using namespace std;
class Student {
public:
// Default constructor
Student() : name("Unknown"), age(0)
{
}
// Parameterized constructor
Student(const string& n, int a) : name(n), age(a)
{
}
// Method to display student information
void display() const
{
cout << "Name: " << name << ", Age: " << age << endl;
}
private:
string name;
int age;
};
int main()
{
Output:
Name: Unknown, Age: 0
Name: Alice, Age: 20
Name: Bob, Age: 22
Name: Charlie, Age: 21
Explanation:
In the C++ code example-
1. We first define a class named Student with two attributes/ private
data members- name and age.
2. The class has a default constructor that initializes attributes to
default values and a parameterized constructor that takes values
for name and age.
3. Inside the main() function, we declare an array of Strudent
objects named students with a size of 3. This allocates memory for
three Student objects but doesn't initialize them yet.
We then call the display() function on the first element of the
array of objects, i.e., display().students[0].
As a result, the default constructor is called for each element
in the students array initializing name with value "Unknown" and
age with value 0.
The values for the first object are printed to the console.
4. We then create three individual Student objects (s1, s2, and s3)
using the parameterized constructor. These objects have specific
values for name and age:
The first object has the name Aditi and age 20, and the second
object has the name Josh and age 22.
The third object has name Deepak and age 21.
5. After that, we declare another array named studentsWithValues of
Student objects initialized using parameterized objects (s1, s2, and s3).
This creates a new array with objects that have specific values.
6. We then call the display() method on individual objects to show their
attributes.
7. This is done using a for loop that begins with the control variable I set
to zero and continues till I <3. After every iteration, the value of i is
incremented by 1.
The array students shows the default values, while the array
studentsWithValues displays the initialized values. The output demonstrates
the usage of constructors and arrays of objects.
Constructor In C++ With Default Arguments
A constructor with default arguments allows you to provide default values for
some or all of its parameters. This means that you can skip some arguments
from the constructor when creating an object, and the constructor will still
use the default values (garbage values) for those arguments. This gives you
freedom when creating objects and lets you avoid explicitly giving values for
parameters that frequently have the same value.
Syntax:
class ClassName {
Access_specifier:
// Constructor with default arguments
ClassName(data_type parameter1 = default_value1, type parameter2 =
default_value2, ...);
};
Here,
The class keyword defines the class whose name is given
by className, which is the same as the name of its constructor.
The access_specifier reflects the visibility of the class members.
The paramter1, data_type, and default_value1 refer to the name of
the parameters, its type, and its default value, respectively.
Code Example:
#include <iostream>
using namespace std;
class Rectangle {
private:
int width;
int height;
public:
// Constructor with default arguments
Rectangle(int w = 0, int h = 0) : width(w), height(h) {}
void displayDimensions() {
cout << "Width: " << width << ", Height: " << height << endl;
}
};
int main() {
Rectangle rect1; // Default constructor in C++ with both arguments as defaul
t
Rectangle rect2(5); // Default constructor in C++ with height as default
Rectangle rect3(3, 8); // Constructor with provided width and height
Output:
Width: 0, Height: 0
Width: 5, Height: 0
Width: 3, Height: 8
Explanation:
1. Here, we first define a Rectangle class with a constructor that has
default arguments for both width and height, i.e., (int w = 0, int h =
0).
2. This constructor allows object creation with default values (here, 0) for
width and height if no arguments are provided during object creation.
3. In the main() function, we create three Rectangle objects as follows:
Object rect1 is created using the default constructor, resulting in
both width and height being set to 0.
Object rect2 is created with a width of 5 and the default height
of 0,
Object rect3 is created with a width of 3 and a height of 8.
4. For each object, we then call the displayDimensions() method to
display the width and height of the respective rectangles.
Initializer List For Constructor In C++
Syntax:
class ClassName
{
access_specifier:
// Default constructor
ClassName()
{
}
// Parameterized constructor with 1 parameter
ClassName(parameter_type parameter1)
{
}
// Parameterized constructor with 2 parameters
ClassName(parameter_type parameter1, parameter_type parameter2)
{
}
};
Here,
The class keyword is used to define the class.
className is the name of the class in which the constructor is defined,
as well as the name of all constructors, i.e., default and parameterized.
The access_specifier specifies the visibility of the data members and
member functions in the class.
The term parameter refers to the parameter taken by the constructor.
And parameter_type refers to their data type.
className(parameter_type parameter1): It is a parametrized
constructor with 1 parameter.
className(parameter_type parameter1,parameter_type
parameter2): It is a parametrized constructor with 2 parameters.
❏ While creating the object, arguments must be passed to let compiler
know, which constructor needs to be called.
❏ While creating the object, arguments must be passed to let compiler
know, which constructor needs to be called.
#include <iostream>
#include <string>
class Student {
public:
// Default constructor
Student() {
name = "Unknown";
age = 0;
}
// Parameterized constructor #1
Student(string n) {
name = n;
age = 0;
}
// Parameterized constructor #2
Student(string n, int a) {
name = n;
age = a;
}
private:
string name;
int age;
};
int main() {
// Creating Student objects using different constructors
Student student1; // Using the default constructor
Student student2("Anya"); // Using parameterized constructor #1
Student student3("Harsh", 20); // Using parameterized constructor #2
class Student {
public:
// Default constructor
Student() {
name = "Default";
age = 0;
}
// Parameterized constructor
Student(const string n, int a) {
name = n;
age = a;
}
// Copy constructor
Student(const Student& other) {
name = other.name;
age = other.age;
}
int main() {
// Using the constructors
Student student1; // Default constructor
Student student2("Unstop", 5); // Parameterized constructor
Student student3 = student2; // Copy constructor
// Displaying details
student1.display();
student2.display();
student3.display();
return 0;
}
Output:
Name: Default, Age: 0
Name: Unstop, Age: 5
Name: Unstop, Age: 5
Explanation:
In this еxamplе,
1. We dеfinе a Studеnt class after including required header files and
using the namespace std.
2. The class contains a dеfault constructor, a paramеtеrizеd constructor,
and a copy constructor, as follows-
Thе dеfault constructor sеts a studеnt's name to Unknown and
agе to 0. This ensures that any object in class Student crеatеd
without spеcifying a namе and agе starts with thеsе dеfault
valuеs.
Thе paramеtеrizеd constructor allows us to spеcify thе
studеnt's namе and agе during thе crеation of a studеnt objеct,
offеring customization.
Thе copy constructor pеrmits thе crеation of a nеw studеnt by
rеplicating thе dеtails of an еxisting onе.
3. In the main() function, wе first crеatе and display thrее diffеrеnt
Studеnt objеcts using thе various constructors, as follows-
First is object student1, which invokes the default constructor,
Then, student2 object invokes the parametrized constructor,
which assigns values Unstop for name and 5 for age.
Last, student3, which holds the copy of student2, thus invoking
the copy constructor.
4. We then call the display() function for each object to print its details.
The output will display the details of the three students respectively.
How Does Constructor in C++ Differ From Normal Member Function?
Constructors and member functions both belong to a class but serve
different purposes and have distinct characteristics.
Constructors in C++ classes are special member functions used to
initialize objects of a class. They are automatically called when an object
is created, setting up the initial state of the object. On the other
hand, member functions define the operations that can be performed on
objects of the class and need to be called explicitly.
The table below highlights the difference between a member function and
a constructor in C++.
Aspect Constructor Member function
Purpose Initialize object properties/ Define operations and
attributes during creation behaviors of objects
Naming Same as the class name Any valid function name
Return No return type (not even void) Can have any valid return type
type
Argume Can accept parameters only for Can accept parameters for
nts initialization operation
Invocati Constructor in C++ is Member functions must be
on automatically invoked during called explicitly using the
object creation object name
Overloa Can have multiple overloads Can have multiple overloads
ds
Initializa Primarily focused on initial state Perform operations on data
tion setup members
Benefits Of Using Constructor:
Some of the key benefits of using the
constructor in C++ are as follows:
Initialization: A constructor in C++ provides a mechanism to initialize an
object's data members when it is created, ensuring the entity starts in a
valid and consistent state.
Customization: Parameterized constructor in C++ allows distinct objects
to be initialized with specific values, enabling customization based on the
requirements of the program.
Readability of the Code: Constructors in C++ improve code readability
by centralizing object initialization logic with the class specification rather
than spreading it throughout the program.
Default Values: Implicit default constructor in C++ allow object creation
with default values for their data members, especially when some values
are commonly used.
Resource Management: When creating an object, constructors in C++
can be used to allocate and control resources (dynamic memory),
ensuring that they are properly cleaned up after the object's lifespan.
Object Initialization: A constructor in C++ offers a simple mechanism
to guarantee that an object's setup is completed at creation, preventing
uninitialized or inconsistent states.
Constructor Overloading: Using multiple constructors with different
argument lists allows objects to be created and initialized in a variety of
ways, adapting to different scenarios.
Inheritance and Derived Classes: Constructors facilitate inheritance by
allowing derived classes to use base class constructors in C++ for
initializing base class objects and data members, ensuring proper object
creation across the inheritance hierarchy.
Code Reuse: By using constructors to generate many objects with
related properties, unnecessary code can be reduced, and maintainability
is improved.
Destructor in C++
A destructor is a special member function that is called automatically
when an object goes out of scope or when we delete the object with the
delete expression.
The primary purpose of a destructor is to perform clean-up operations,
such as releasing resources, closing files, or freeing dynamically allocated
memory, ensuring that the object’s termination is handled gracefully.
In C++, a destructor has the same name as that of the class, and it does
not have a return type. A tilde (~) precedes the identifier to indicate
destructor.
For example:
class Wall
{
public:
// create a destructor
~Wall()
{
// code
}
};
Here, ~Wall() is a destructor of the class Wall.
Syntax Of Destructor:
class MyClass
{
public:
// Constructor
MyClass()
{
}
// Destructor
~MyClass()
{
// Destructor code here
}
// Other member functions and data members
};
Here,
MyClass is the name of the class being defined using the class keyword.
MyClass() represents the constructor, which is called when a local
variable of MyClass is defined and initialized.
The term ~MyClass() refers to the destructor, whose code is executed
when an object is destroyed.
Both the constructor and the destructor in C++ play key responsibilities in
effectively handling object creation and destruction, ensuring correct
resource management and cleaning throughout the object's lifespan.
Example:
#include<iostream>
using namespace std;
class Demo
{
private:
int num1, num2;
public:
Demo(int n1, int n2)
{
cout<<"Inside Constructor"<<endl;
num1 = n1;
num2 = n2;
}
void display()
{
cout<<"num1 = "<< num1 <<endl;
cout<<"num2 = "<< num2 <<endl;
}
~Demo()
{
cout<<"Inside Destructor";
}
};
int main()
{
Demo obj1(10, 20);
obj1.display();
return 0;
}
Output
Inside Constructor
num1 = 10
num2 = 20
Inside Destructor
In the above program, the class Demo contains a parameterized constructor
that initializes num1 and num2 with the values provided by n1 and n2. It also
contains a function display() that prints the value of num1 and num2. There
is also a destructor in Demo that is called when the scope of the class object
is ended. The code snippet for this is given as follows.
class Demo
{
private:
int num1, num2;
public:
Demo(int n1, int n2)
{
cout<<"Inside Constructor"<<endl;
num1 = n1;
num2 = n2;
}
void display()
{
cout<<"num1 = "<< num1 <<endl;
cout<<"num2 = "<< num2 <<endl;
}
~Demo()
{
cout<<"Inside Destructor";
}
};
The function main() contains the object definition for an object of class type
Demo. Then the function display() is called. This is shown below.
Demo obj1(10, 20);
obj1.display();
Syntax Of Destructor Outside Class:
The following syntax can be used to
specify the destructor for a C++ class outside of the class:
class MyClass
{
public:
~MyClass(); // Destructor declaration
// Other member functions and data members
};
// Destructor definition outside of the class
MyClass::~MyClass()
{
// Destructor code here
}
Here,
class MyClass is the name of the class being defined using
the class keyword.
~MyClass(); is the declaration of the destructor within the class,
indicating that a destructor exists but not providing its
implementation.
MyClass::~MyClass() is the definition of the destructor outside
the class, using the scope resolution operator (::) to specify that
this destructor belongs to MyClass.
Rules For Defining A Destructor In C++
When defining a destructor in C++, there are several important rules to
follow to ensure it functions correctly and efficiently:
Name and Syntax of The Destructor In C++: The destructor must
have the same name as the class, prefixed with a tilde (~). Also, it
cannot have any return type, not even void, and it cannot take any
arguments. For Example-
class MyClass {
public:
~MyClass() {
// Destructor code here
}
};
Only One Destructor: A class can have only one destructor in C++. You
cannot overload a destructor, meaning you cannot have multiple
destructors with different parameters.
No Explicit Call: You should not explicitly call a destructor on an object.
Instead, let the compiler manage the destruction process. Manually
calling a destructor can lead to undefined behavior, especially if the
object is later deleted again. For Example-
MyClass obj;
// obj.~MyClass(); // Not recommended, let the compiler handle it
No Exception Throwing: Destructors should not throw exceptions. If an
exception is thrown during a destructor’s execution and another
exception is already active, the program will terminate. To handle
exceptions, use a try-catch block within the destructor. For Example-
~MyClass() {
try {
// Code that might throw an exception
} catch (...) {
// Handle exceptions
}
}
Destructor for Non-Resource Classes: If a class does not acquire
resources that need explicit release (e.g., memory, file handles), it may
not require a custom destructor. The compiler will provide a default
destructor if none is defined.
Destructor Order in Inheritance: In a class hierarchy, destructors are
called in the reverse order of the constructor calls. The derived class’s
destructor is called first, followed by the base class destructor.
These guidelines serve as the basis for the definition of a destructor in C++.
They contribute to effective object decomposition and resource
management, which results in more dependable and maintainable code.
When is a Destructor Called?
The destructor in C++ may be called automatically in any of the following situations:
1. When an object exits scope: The destructor of the object is automatically
invoked before it is destroyed when the block of code in which it is declared
ends. This guarantees that all cleaning and resource release procedures are
carried out.
2. When an object is explicitly deleted: After an object is created dynamically
with the new operator, it may be deliberately destroyed by calling its destructor
in C++ with the delete keyword. This deals with memory deallocation.
3. When an object belongs to a different class: The destructors of all the
objects that are members of the outer class are automatically invoked in the
reverse order of their declarations when the destructor of an object that is a
member of that class is called.
4. When a container is cleaned or destroyed while an object is inside: The
destructors of all the objects included in a container, such as a vector or list, are
automatically called when the container is cleared or destroyed.
5. When a smart pointer is used with an object: When a smart pointer itself
exits its scope or is explicitly reset, it automatically calls the destructor of the
object to which it is addressed. Examples of such smart pointers are
std::shared_ptr and std::unique_ptr.
6. When an item of a derived class is destroyed: If both a base class and a
derived class have destructors, the destructor of the base class is called first
when an object of the derived class is destroyed.
It is important to note that in C++, the runtime environment automatically handles
destructor invocation in these situations, which helps in managing resources and
avoiding memory leaks. This automatic management supports effective resource
handling and simplifies the programmer's task.
Types of Destructors:
There are two ways (types) of destructors. They
are,
1. Default Destructor
2. User-Defined Destructor
Default Destructor:
If you do not explicitly define a destructor in C++
classes, the compiler automatically generates a default destructor. It
performs basic cleanup tasks by invoking the destructors of member objects
and base classes. It is suitable for classes that do not manage dynamic
memory or other resources needing explicit cleanup.
Characteristics Of Default Destructor:
Automatic Creation: If no destructor is explicitly defined, the compiler
generates a default destructor in C++ programs.
Basic Cleanup: It does not handle complex resource management or
custom cleanup beyond calling destructors of member objects and base
classes.
Implicit: You don't see it in the class definition unless you specifically
declare it, but it's always there if you don't provide one.
Let's look at an example that illustrates the use of a default constructor in
C++ code.
Example:
#include <iostream>
class DefaultDestructorDemo
{
public:
// Constructor
DefaultDestructorDemo()
{
std::cout << "Constructor called." << std::endl;
}
// No need to define a destructor; the compiler provides a default one.
};
int main()
{
std::cout << "Creating an object." << std::endl;
DefaultDestructorDemo obj; // Creating an object of the class
std::cout << "Object created." << std::endl;
// The object goes out of scope at the end of the block
return 0;
}
Output:
Creating an object.
Constructor called.
Object created.
Explanation:
In the above code example-
1. We start by defining a class called DefaultDestructorDemo with an
explicit public constructor.
2. In the constructor, we use std::cout to print the message to the console
indicating when the constructor is being executed.
3. In this class, we don’t explicitly define a destructor. Since we don’t
provide one, the C++ compiler automatically generates a default
destructor.
4. The default destructor will be called when an object of
DefaultDestructorDemo goes out of scope, but it doesn’t perform
any special cleanup beyond the basic tasks provided by the compiler.
5. In the main() function, we start by printing a message to the console
indicating that we are about to create an object of
the DefaultDestructorDemo class.
6. We then declare an object named obj of type DefaultDestructorDemo.
This triggers the constructor, and the corresponding message is printed
to the console.
7. After the object is created, we print another message to the console to
signal that the object creation process is complete.
8. As the main() function reaches its end, the obj goes out of scope. Here,
the default destructor automatically generated by the compiler is called.
9. Finally, the main() function returns 0, which indicates that the program
has been executed successfully.
User-Defined Destructor:
A user-defined destructor is explicitly declared
and defined by the programmer. It allows for custom cleanup and resource
management when an object is destroyed. It is necessary for classes that
manage dynamic resources, like memory allocated with new operator, file
handles, or network connections.
Characteristics of User-Defined Destructor:
Explicit Definition: You define it in your class to handle specific
resource management tasks, such as releasing memory or closing files.
Custom Cleanup: Allows for complex resource management and
custom actions to be performed when the object is destroyed.
Overriding: If a user-defined destructor is provided, it overrides the
default destructor in C++ classes.
Code Example:
#include <iostream>
class UserDefinedDestructorDemo
{
public:
// Constructor
UserDefinedDestructorDemo()
{
std::cout << "Constructor called." << std::endl;}
// User-defined destructor
~UserDefinedDestructorDemo()
{
std::cout << "User-defined destructor called." << std::endl;
// Additional cleanup or resource release can be performed here
}
};
int main()
{
std::cout << "Creating an object." << std::endl;
UserDefinedDestructorDemo obj; // Creating base class object
std::cout << "Object created." << std::endl;
std::cout << "Exiting the scope." << std::endl;
// The object goes out of scope at the end of the block, invoking the
destructor
std::cout << "End of program." << std::endl;
return 0;
}
Output:
Creating an object.
Constructor called.
Object created.
Exiting the scope.
User-defined destructor called.
End of program.
Explanation:
In the above C++ code example-
1. We define a class called UserDefinedDestructorDemo containing a
public constructor and a user-defined destructor.
2. The public constructor UserDefinedDestructorDemo() is a special
member function that is called automatically when a class object is
created. It uses std::cout to print a message to the console, letting us
know when the constructor is being executed.
3. We also define a user-defined destructor
~UserDefinedDestructorDemo(), which is called automatically when
an object of class goes out of scope or is explicitly deleted. When called,
it prints a message indicating the same.
4. In the main() function, we first print a message to the console to
indicate that we are about to create an object of
the UserDefinedDestructorDemo class.
5. We then declare an object named obj of type
UserDefinedDestructorDemo. This action triggers the constructor, so a
message is printed to the console, confirming that the object has been
created.
6. After the object is created, we print a message to the console to signal
that the object creation process is complete.
7. We then print another message to indicate that we are approaching
the end of the main() function, where the object obj will go out of
scope.
8. As the main() function reaches its end, the object obj goes out of
scope, which automatically triggers the user-defined destructor.
Therefore a message is printed to the console, confirming that the
destructor is being executed.
Key Properties of Destructor:
Some of the important characteristics of
a destructor in C++:
Automatic Invocation: When an automatic object leaves its scope or
when a dynamically created object is deallocated using the delete
operator, the destructor is automatically executed. The C++ runtime
handles the destructor, i.e., there is no need to call it explicitly.
Name: The destructor has the same name as the class, but it is preceded
by the tilde symbol (~). For instance, the destructor will be called
~MyClass() if the class is called MyClass.
No Return Type or Arguments: Destructors don't accept any
arguments and don't even have a void return type. They can't have too
many distinct signatures on them.
Number Of Destructors: Each class may only have one destructor in
C++, which may be known as a single destructor. The compiler will build
a default destructor if a class doesn't declare one. However, it's crucial to
create a unique destructor if the class handles resource management or
cleaning tasks.
Derived Classes Inherit from Base Classes: When an object
belonging to a derived class is destroyed, the base class's destructor will
be immediately invoked. The destructor of the derived class is called
after the destructor of the base class.
Virtual Destructors for Polymorphic Behavior: It is frequently
required to specify the base class destructor in C++ as virtual
when promoting inheritance and polymorphism. This avoids memory
leaks and unexpected behavior by ensuring that the appropriate
destructor of the derived class is invoked when removing an object using
a base class reference.
Order Of Destruction: In the reverse order of their declarations, the
member variables' destructors are invoked first when an object exits its
scope. After that, the class's destructor is used.
Implicitly Declared If Not Defined: If a class doesn't explicitly declare
a destructor in C++, the compiler will construct one for it. To guarantee
appropriate cleanup, it's recommended to create a custom destructor if
the class maintains resources like dynamic memory allocations or file
handles.
Explicit Destructor Calls:
Explicit destructor calls refer to the manual
invocation of a destructor for an object, rather than relying on the automatic
destructor call that happens when an object goes out of scope or is deleted.
Normally, destructors are automatically invoked when an object’s lifetime
ends, but in certain cases, we might want to call the destructor explicitly.
This can occur in scenarios like:
Manual Memory Management: When dynamically allocating objects in
C++ using a new operator (which allows constructing objects in pre-
allocated memory), it becomes necessary to manually call the destructor
to ensure proper resource cleanup. This is because the delete operator
won’t be used, and thus the destructor won’t be automatically called.
Resource Management in Custom Allocators: If you're implementing
custom memory management or object pools, you might need to call
destructors explicitly to ensure that objects release their resources
before being returned to the pool or deallocated.
Explicit Destructor Calls: In rare cases, you might explicitly need to
call a destructor. This can be done by using the following syntax:
obj.~ClassName();
Here, obj is an instance of ClassName. Although this is technically possible,
it is generally discouraged because it can lead to undefined or unexpected
behaviour if not handled carefully.
Potential Issues with Explicit Destructor Calls:
Explicitly invoking a
destructor can lead to the following issues:
1. Destructed State: If you call a destructor explicitly and then attempt to
use the object again, the object will be in a destructed state. This can
lead to unpredictable behaviour and bugs.
2. Inheritance Issues: In a class hierarchy, explicitly calling a destructor
might not correctly follow the inheritance chain. This could result in
base class destructors not being called, leading to incomplete
cleanup and potential resource leaks.
3. Placement New and Manual Memory Management: In scenarios
involving placement new, where you construct an object in a pre-
allocated memory block, you might need to call the destructor explicitly
before reusing or freeing that memory. This is one of the few cases
where explicit destructor calls are justified.
In short, we discourage explicit calls to destructor in C++ programs because
they run the risk of causing unexpected behavior and making manual
memory management more difficult. The code will be more predictable
and manageable if destructors are called automatically and correct memory
management techniques are used.
Destructor Overloading:
Destructors cannot be overloaded, because
destructors have a fixed signature. they take no parameters and do not
return any value. A class can only have one destructor, which is
automatically called when an object of the class is destroyed.
The purpose of a destructor is to perform clean-up tasks, such as
releasing resources or freeing memory. Since the language does not
allow destructors to have parameters, there is no scope for having multiple
destructors with different parameter lists, which is the essence of function
overloading. Below is an example illustrating why a class cannot have
multiple destructors.
Example:
class MyClass
{
public:
// Constructor
MyClass()
{
// Constructor code
}
// Destructor
~MyClass()
{
// Destructor code
}
// Uncommenting the following code will result in a compilation error
/*
~MyClass(int x) { // Invalid: Destructor cannot take parameters
// Destructor code
}
*/
};
int main()
{
MyClass obj; // Destructor ~MyClass() is called automatically when obj goes
out of scope
return 0;
}
Explanation:
We are attempting to define a second destructor ~MyClass(int x). This
would result in a compilation error because C++ does not support
destructor overloading. Only one destructor without parameters is
allowed per class definition.