The programming world has a tremendous growth in the adaptability of Object-oriented programming structure.
This programming strategy ensures proper code structure, code readability, code reusability, etc. Earlier, programmers have to face the issue related to bad read, i.e., garbage value. It usually occurs when they forget to initialize the member variable of a Class.
So, developers found the strategy that will initialize variables and destroy them when variables go out of the function. They coined this strategy as Constructors and Destructors.
Programmers further developed different constructors to support different cases that will allow them to handle different test scenarios. In this article, we will explain those strategies and the code samples showing their working pattern.
What is a Constructor in C++?
Like normal member functions, Constructor in C++ is a member function that is mainly useful to initialize member variables. In C++, Constructor is called by default when the object of the respective class is created in the primary method.
Always remember that a constructor does not have any return type, and there will be no void as well.
In C++, if we don't create a constructor explicitly, then the compiler generates a default constructor for us. Following is the code showing the way to create and define a constructor:
// C++ program to demonstrate the
// concept of Constructors
#include <bits/stdc++.h>
using namespace std;
class default_construct
{
public:
int var1, var2;
//Default Constructor with name same as class name...
default_construct()
{
var1 = 1;
var2 = 2;
}
};
int main()
{
//Calling default constructor automatically upon object creation...
default_construct c;
cout << "var1: " << c.var1 << endl
<< "var2: " << c.var2;
return 1;
}
Output:
var1: 1
var2: 2
Types of Constructor in C++
In C++, there are three types of constructors which support different use-cases. The following are:
- Default Constructor.
- Parameterized Constructor
- Copy Constructor.
1) Default Constructor
A default constructor does not take any parameter or arguments. The above code snippet is one of the simplest examples of a Default constructor. From above, we can see the constructor does not accept any argument and also does not have any return type.
The Default constructor's main purpose is to initialize member variables of the class to avoid garbage reading of variables during main program execution.
If a programmer does not provide a default constructor inside their class, then the C++ compiler does this for them by assigning all the variables with a null value. E.g., 0 for integer, null to string variable type.
Use of Default Constructor
- In a C++ program, some condition or scenario might occur in which a program tries to access the member variable, but the variable is not initialized yet.
- This may lead to reading the garbage collection, and it will cause unexpected output.
- A default constructor is implemented to initialize the member variable with some default value to avoid null pointer reference and unexpected output.
- As the users need not call explicitly default constructors, it is handy for the users to initialize member variables during class object declaration.
2) Parameterized Constructor
Parameterized constructor enables the programmers to initialize the member function with the default values of their choices. Through a Parameterized constructor, a programmer can assign the values they wish to different every time they create an instance of the respective class.
Those values can be passed as parameters while creating the class object. Below is the code example illustrating the definition of the parameterized constructor:
// C++ program to demonstrate
// parameterized constructors
#include <bits/stdc++.h>
using namespace std;
class Param_constructor
{
private:
int a, b;
public:
// Parameterized Constructor
Param_constructor(int x1, int y1)
{
a = x1;
b = y1;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
};
int main()
{
// Constructor called
Param_constructor p1(1, 1);
Param_constructor p2(2, 2);
// Access values assigned by constructor
cout << "p1.x = " << p1.getA() << ", p1.y = " << p1.getB();
cout << "p2.x = " << p2.getA() << ", p2.y = " << p2.getB();
return 0;
}
Output:
p1.x = 1, p1.y = 1p2.x = 2, p2.y = 2
Use of Parameterized Constructor
- Using a parameterized constructor, the programmer can initialize the objects with different values every time he creates different objects.
- The programmer needs to pass the values as an argument to initialize the member variables during object creation.
- Parameterized constructor introduces a concept of constructor overloading.
- Constructor overloading is the technique through which a programmer can define more than one parameterized constructor with a different number of parameters to enable the compiler to decide which one to call.
3) Copy Constructor
Copy constructors are a type of constructors that work to initialize an object with the help of another object which is present in the same class type.
An object of the same class needs to be passed as an argument while initializing the object in the main method.
The below code snippet will help us explain the working of the copy constructor more comprehensively:
#include<iostream>
using namespace std;
class CopyConstructor
{
private:
int a, b;
public:
CopyConstructor(int x1, int y1) { a = x1; b = y1; }
// Copy constructor defined..
CopyConstructor(const CopyConstructor &p1) {a = p1.a; b = p1.b; }
int getA() { return a; }
int getB() { return b; }
};
int main()
{
CopyConstructor p1(1, 2); // Calling Normal constructor here
CopyConstructor p2 = p1; // initializing new instance of class using p1 object type
cout << "p1.x = " << p1.getA() << ", p1.y = " << p1.getA();
cout << "\np2.x = " << p2.getA() << ", p2.y = " << p2.getB();
return 0;
}
Output:
p1.x = 1, p1.y = 1
p2.x = 1, p2.y = 2
Use of Copy Constructor
- Copy constructors have usually been used to initialize the variable of type object by calling the constructor bypassing the object of the same type as an argument.
- Copy constructor introduced the concept of Shallow copy and deep copy.
a) Shallow Copy
An object is generated using shallow copy by directly copy-pasting the data of all variables from the original object. This works as expected if none of the class object variables exist in the heap memory.
If any object is present in the heap section, then the newly created object and the one present in a heap will point to the same object by reference. This can lead to uncertainty and potential run-time errors.
So, this many times leads to uncertainty and inconsistency in data. The below code illustrates the phenomenon of Shallow copy:#include <bits/stdc++.h>
b) Deep Copy
Deep copy generates an object by copying data from all member variables and allocating identical memory resources (separate address in memory stack from that of previous one) to the object with the same value.
To execute Deep copy, we must specifically describe the copy function Object() and, if possible, allocate dynamic memory. It's also necessary to assign memory to the variables in the other constructors dynamically.
Once you execute the above syntax in your system, you will get the out like this:
In this way, unlike shallow copy deep copy makes a different object point to different addresses. So, making a change to one of them will not affect the other object’s member variables. To avoid object replication, we implement deep copy instead of Shallow copy.
Constructor Overloading
Constructor overloading is a special type of method overloading in which we define more than one constructor with the same name (obvious) but with a different number of arguments or the same number of arguments with different data types.
A different number of arguments helps the compiler at the runtime identify which one among the constructors has been called by the user. Below is a code snippet showing how to do Constructor overloading:
// C++ program to demonstrate
// Constructor overloading
#include <bits/stdc++.h>
using namespace std;
class Overloading
{
public:
float length;
// Constructor with no parameters
Overloading()
{
length = 0;
}
// Constructor with two parameters
Overloading(int a, int b)
{
length = a + b;
}
Overloading(int a, int b, int c)
{
length = a + b + c;
}
void show()
{
cout<< length << "\n";
}
};
int main()
{
/// Constructor Overloading
/// with three different constructors
/// of class name
Overloading o1;
Overloading o2( 10, 20); //calling Constructor
Overloading o3( 10, 20, 30);
o1.show();
o2.show();
o3.show();
return 0;
}
From the above code, we can see that the Overloading class contains three different constructor methods with different function signatures. Constructor overloading allows the programmer to call different versions of constructors.
Output:
0
30
60
Bypassing the different number of arguments or different data types helps the compiler decide which constructor is called runtime. This makes it easy for the programmer to initialize the member variables differently, thus making the program easier to manage and increase readability.
What is Destructor?
Unlike Constructor, destructor is also a member function called by the compiler by default to delete an object. The system will automatically call them when all objects are destroyed. Hence, they don't have any of the return type and can't be declared const or static members.
The Destructor address cannot be accessed by the user or program executing it. It is declared the same as Constructor without any parameters preceding with the "~" symbol. There can be only one destructor in a class definition. Following is the sample code illustrating the definition of destructor:
#include <iostream>
using namespace std;
class Student{
public:
Student (){
cout<<"Invoking Constructor"<< "\n";
}
~Student (){
cout<<"Invoking Destructor"<< "\n";
}
};
int main(void){
Student s1;
Student s2;
return 0;
}
Output:
Invoking Constructor
Invoking Constructor
Invoking Destructor
Invoking Destructor
Constructor Vs Destructor: Comparison Table
Constructor | Destructor |
---|---|
It is used to initialize member variables. | It is used to "destruct objects" of the class. |
While its declaration, we can pass arguments. | We cannot pass any argument to it. |
A constructor is called while creating a class instance. | Destructor is called when an instance goes out of scope. |
Allocates the memory, for instance. | Deallocates the memory of an instance. |
Constructor overloading is allowed. | Overloading is not possible. |
The name will be the same as that specific class. | The name is the same but followed by the "~" prefix. |
A class can consist of any number of the constructor. | A class only requires to have a solo destructor. |
The system can't overload the constructors. | The system can't overload the destructors. |
Conclusion
Through this article, we discussed in detail the Constructors, their various types, their use cases, along their code implementation. We can conclude that by using constructors, we can initialize object variables and use destructors.
We can destroy the object or instance and deallocate the memory space. It is usually considered a good coding practice among the developers to use Constructors and destructors while implementing OOPS concepts. It reduces the chance of variables storing garbage values and improves code understandability.