Bytebook Compiler

Classes & Objects

Constructors & Destructors

Initialise objects automatically: default, parameterised and copy constructors, initialiser lists, constructor overloading, and destructors with the order they run in.

5 min read5 runnable examples4-question quiz

A constructor is a special member function that runs automatically when an object is created. Its job is to put the object into a valid starting state.

  • Same name as the class.
  • No return type — not even void.
  • Can be overloaded.

Default and parameterised constructors

C++
#include <iostream>
#include <string>
using namespace std;

class Student {
    string name;
    int roll;
public:
    Student() {                                   // default constructor: no arguments
        name = "Unknown";
        roll = 0;
        cout << "default constructor" << endl;
    }
    Student(string n, int r) {                    // parameterised constructor
        name = n;
        roll = r;
        cout << "parameterised constructor for " << n << endl;
    }
    void show() { cout << roll << ": " << name << endl; }
};

int main() {
    Student a;                    // calls Student()
    Student b("Meera", 12);       // calls Student(string, int)
    Student c = {"Kiran", 13};    // also the parameterised one
    a.show(); b.show(); c.show();
    return 0;
}
Output
default constructor
parameterised constructor for Meera
parameterised constructor for Kiran
0: Unknown
12: Meera
13: Kiran

Initialiser lists

The preferred way to initialise members is a member initialiser list, after a colon. It initialises members directly instead of assigning them afterwards, and it is required for const members, reference members and base classes.

C++
#include <iostream>
using namespace std;

class Circle {
    const double PI;           // const members must use the initialiser list
    double radius;
public:
    Circle(double r = 1.0) : PI(3.14159), radius(r) {}    // also a default constructor
    double area() const { return PI * radius * radius; }
};

int main() {
    Circle unit, big(10);
    cout << unit.area() << " " << big.area() << endl;
    return 0;
}
Output
3.14159 314.159

Copy constructor

The copy constructor creates a new object as a copy of an existing one. It takes a const reference to an object of the same class. It runs when you:

  1. initialise one object from another: Point b = a; or Point b(a);
  2. pass an object by value to a function;
  3. return an object by value (often optimised away).
C++
#include <iostream>
using namespace std;

class Point {
    int x, y;
public:
    Point(int x, int y) : x(x), y(y) {}
    Point(const Point &other) : x(other.x), y(other.y) {     // copy constructor
        cout << "copying (" << x << ", " << y << ")" << endl;
    }
    void show() const { cout << "(" << x << ", " << y << ")" << endl; }
};

void display(Point p) { p.show(); }          // by value: copies

int main() {
    Point a(3, 4);
    Point b = a;                             // copy constructor
    display(a);                              // copy constructor again
    b.show();
    return 0;
}
Output
copying (3, 4)
copying (3, 4)
(3, 4)
(3, 4)

Destructors

A destructor runs automatically when an object is destroyed — when it goes out of scope, or when you delete it. Its name is the class name with a tilde: ~Student(). It takes no arguments, returns nothing, and cannot be overloaded. Use it to release resources: memory, files, network connections.

C++
#include <iostream>
#include <string>
using namespace std;

class Tracer {
    string name;
public:
    Tracer(string n) : name(n) { cout << "  construct " << name << endl; }
    ~Tracer()                  { cout << "  destroy   " << name << endl; }
};

Tracer global("global");

int main() {
    cout << "main starts" << endl;
    Tracer a("a");
    {
        Tracer b("b");
        Tracer c("c");
        cout << "  leaving inner block" << endl;
    }                                       // c then b destroyed here
    Tracer *d = new Tracer("d (heap)");
    delete d;                               // destroyed only when deleted
    cout << "main ends" << endl;
    return 0;
}                                           // a destroyed, then global
Output
  construct global
main starts
  construct a
  construct b
  construct c
  leaving inner block
  destroy   c
  destroy   b
  construct d (heap)
  destroy   d (heap)
main ends
  destroy   a
  destroy   global

Objects are destroyed in the reverse order of construction — like a stack.

Managing a resource: a dynamic array class

C++
#include <iostream>
using namespace std;

class IntArray {
    int *data;
    int size;
public:
    IntArray(int n) : data(new int[n]()), size(n) {}          // () zero-initialises
    IntArray(const IntArray &o) : data(new int[o.size]), size(o.size) {   // deep copy
        for (int i = 0; i < size; i++) data[i] = o.data[i];
    }
    ~IntArray() { delete[] data; }
    int &at(int i) { return data[i]; }
    int length() const { return size; }
};

int main() {
    IntArray a(5);
    for (int i = 0; i < a.length(); i++) a.at(i) = i * i;
    IntArray b = a;                  // deep copy: b gets its own memory
    b.at(0) = 100;
    cout << "a[0] = " << a.at(0) << ", b[0] = " << b.at(0) << endl;
    return 0;
}
Output
a[0] = 0, b[0] = 100

Without the copy constructor, b would share a's pointer (a shallow copy): changing b would change a, and both destructors would delete[] the same memory. More on this in Dynamic Memory & Smart Pointers.

Test yourself

4 questions · pick an answer to check it instantly

  1. 1

    Which is true of constructors?

  2. 2

    When is the copy constructor not called?

  3. 3

    In what order are local objects x, y, z (created in that order) destroyed?

  4. 4

    Which members must be initialised in the initialiser list?