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;
}

References

Classes