Introduction
Constructor and Destructor in C++ are special class functions. Constructor initialize values to object members after storage is allocated to the object. Destructor is used to destroy the class object.
Constructor
Compiler calls constructor when a new object of a class is created, allowing the class to initialize member variables. It is declared just like a regular member function, with a name that matches the class name and without any return type. It can be overloaded in a similar way as function overloading. Depending upon the number and type of arguments passed, specific constructor is called.
class Rectangle { int width; int height; public: // Default Constructor Rectangle() { width = 0; height = 0; } // Parametrized Constructor Rectangle(int, int); int area() { return width * height; } }; Rectangle::Rectangle(int w, int h) { width = w; height = h; } // Using initialization list Rectangle::Rectangle(int a) : width(a), height(a) { } int main() { // Invoke Default Constructor Rectangle rect1; // Invoke Parametrized Constructor Rectangle rect2(3, 4); // Uniform initialization Rectangle rect3{ 7, 8}; return 0; }
Default constructor is the constructor which doesn’t take any argument. If a constructor is not defined explicitly, compiler will provide a default constructor implicitly. Parameterized Constructors are the constructors with parameter. Uniform initialization is the same as the functional form, but using {} instead of ().
If you do not explicitly initialize a base class or member that has constructors by calling a constructor, the compiler automatically initializes the base class or member with a default constructor. In the below example, if you leave out the call B2()
in the constructor of class D
(as shown below), a constructor initializer with an empty expression list is automatically created to initialize B2
.
#include <iostream> using namespace std; class B1 { int b; public: B1() { cout << "B1::B1()" << endl; }; // inline constructor B1(int i) : b(i) { cout << "B1::B1(int)" << endl; } }; class B2 { int b; protected: B2() { cout << "B2::B2()" << endl; } // noninline constructor B2(int i); }; // B2 constructor definition including initialization list B2::B2(int i) : b(i) { cout << "B2::B2(int)" << endl; } // Multiple Inheritance class D : public B1, public B2 { int d1, d2; public: // Calls super class constructor D(int i, int j) : B1(i+1), B2(), d1(i) { cout << "D1::D1(int, int)" << endl; d2 = j;} }; int main() { D obj(1, 2); }
Destructor
Syntax for destructor is same as that for the constructor, with a tilde ~ sign as prefix to it. Destructor do not accept any arguments.
class Rectangle { int width; int height; public: // Constructor Rectangle() { width = 0; height = 0; }; // Destructor ~Rectangle(); int area() { return width * height; } }; Rectangle::~Rectangle() { }; int main() { // Allocation on heap Rectangle* rect2 = new Rectangle; // Scope { // Allocation on stack, invoke Default Constructor Rectangle rect1; } delete rect2; return 0; }
Objects in C++ can be created either on the stack or on the heap. Compiler calls destructor when the object goes out of scope. The scope of an object is when it leaves the { } in which it has been declared. This is for local variables, not for static and global one. An arbitrary {} pair enclosing an arbitrary block of code also constitutes a scope. When it is created in heap, it should be destructed by calling delete
on the object.
Copy Constructor
It is used to create a copy of an existing object. A copy constructor has the following general function prototype:
ClassName (const ClassName &old_obj);
Copy constructor may (not guaranteed) be called in following scenarios
- Class object is returned by value.
- Object of the class is passed (to a function) by value as an argument.
- Object is constructed based on another object of the same class.
- Compiler generates a temporary object.
In the above example below code snippet calls copy constructor.
Rectangle rec; // Copy constructor called Rectangle rec2 = rec;
Compiler provides a default copy constructor. Default copy constructor provides a shallow copy i.e. copies references to original objects. Shallow copy constructor not useful when dealing with any dynamically allocated memory. Deep copy allocates separate memory for copied information. Any changes made in one memory location will not affect copy in the other location.
// Example showing Deep Copy class Rectangle { int* width; int* height; public: Rectangle(int w, int h) { // Dynamic memory allocation width = new int; height = new int; *width = w; *height = h; } // Deep Copy Rectangle(const Rectangle& rect) { // Dynamic memory allocation width = new int; height = new int; *width = *rect.width; *height = *rect.height; } void setWH(int w, int h) { *width = w; *height = h; } int area() { return (*height) * (*width); } ~Rectangle() { delete width; delete height; } }; int main() { Rectangle rect(4, 5); // Copy constructor Rectangle rect2 = rect; rect.setWH(4, 6); cout << rect.area() << endl; // Output 24 cout << rect2.area(); // Output 20 return 0; }