Skip to content

Intermediate · Classes, constructors and RAII · topic 14 of 42 · 30 min

Constructors and destructors

Explanation

A constructor is a special member function that runs when an object is created and puts it into a valid state. Use the member initialiser list to set members. A destructor (~ClassName) runs automatically when the object is destroyed, so it is the place to release resources.

Why constructors?

An object should be valid from the very first moment: a Student without a roll number, or a Ticket with a negative fare, should never exist. A constructor is a member function with the same name as the class and no return type. C++ calls it automatically every time an object is created.

cpp
#include <iostream>
#include <stdexcept>
#include <string>

class Ticket {
public:
    Ticket(std::string passenger, int farePaise)
        : passenger_(std::move(passenger)), farePaise_(farePaise) {   // member initialiser list
        if (farePaise_ < 0) throw std::invalid_argument("negative fare");
    }
    void print() const { std::cout << passenger_ << ": " << farePaise_ / 100 << '\n'; }
private:
    std::string passenger_;
    int farePaise_;
};

int main() {
    Ticket t("Asha", 145050);
    t.print();                     // Asha: 1450
    // Ticket bad;                 // error: no default constructor
}
The part after the colon is the member initialiser list. It initialises members directly instead of assigning them later.

The member initialiser list

Members are always initialised BEFORE the constructor body runs. If you assign them inside the body, each member is first default-constructed and then overwritten, which is wasteful. Some members can only be set in the initialiser list: const members, reference members, and members whose type has no default constructor.

Careful: Members are initialised in the order they are DECLARED in the class, not the order you write them in the initialiser list. If one member is computed from another, declare them in the right order. GCC and Clang warn about a mismatched order with -Wall (-Wreorder).

Default constructor and default member values

cpp
class Counter {
public:
    Counter() = default;          // use the default member values below
    explicit Counter(int start) : count_(start) {}
    int value() const { return count_; }
private:
    int count_ = 0;               // default member initialiser (C++11)
};

Counter a;        // 0
Counter b(10);    // 10
Counter c{5};     // 5, brace initialisation
A default constructor takes no arguments. If you write any constructor yourself, the compiler stops generating the default one, so add = default if you still want it.

explicit

A constructor that takes one argument can be used by the compiler to convert silently: void show(Counter c); show(42); would build a Counter from 42 behind your back. Marking the constructor explicit forbids those silent conversions. Make single-argument constructors explicit unless you really want the conversion.

Destructors

cpp
#include <iostream>

class Lamp {
public:
    explicit Lamp(int id) : id_(id) { std::cout << "Lamp " << id_ << " on\n"; }
    ~Lamp() { std::cout << "Lamp " << id_ << " off\n"; }
private:
    int id_;
};

int main() {
    Lamp a(1);
    {
        Lamp b(2);
    }                       // b is destroyed here
    std::cout << "end of main\n";
}                           // a is destroyed here
// Output:
// Lamp 1 on
// Lamp 2 on
// Lamp 2 off
// end of main
// Lamp 1 off
The destructor ~Lamp runs automatically when an object goes out of scope. Objects are destroyed in reverse order of creation.
Think of it like this: A constructor is the check-in desk at a hotel: you get a key and a clean room. The destructor is check-out: the key is returned and the room cleaned, automatically, whatever way you leave the hotel.

A destructor has no parameters and no return type, and a class has exactly one. You almost never call it yourself; C++ calls it at the end of the object's lifetime (end of scope for local objects, delete for heap objects, or when the containing object or container is destroyed).

Common mistakes

  • Writing Ticket t(); to create an object with no arguments. That declares a FUNCTION named t (the "most vexing parse"). Write Ticket t; or Ticket t{};.
  • Forgetting to initialise a member such as an int, leaving garbage. Give every member a default value.
  • Doing heavy work that can fail silently in a constructor. If construction cannot produce a valid object, throw an exception.
  • Throwing exceptions out of a destructor. Destructors are noexcept by default; a throw from one usually ends the program.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Constructors and destructors · 1/6

Constructors

A constructor sets up a new object so it is valid from its very first moment.

Prefer a video by a teacher? Find free videos on “c++ constructors destructors member initializer list explicit” on YouTube ↗

In real life: A Student record that is always valid

A school management system creates a Student object for each admission. The constructor demands a name and a roll number and rejects an empty name, so the rest of the system never has to check whether a Student is half-filled.

cpp
#include <iostream>
#include <stdexcept>
#include <string>

class Student {
public:
    Student(std::string name, int roll) : name_(std::move(name)), roll_(roll) {
        if (name_.empty()) throw std::invalid_argument("name required");
    }
    const std::string& name() const { return name_; }
    int roll() const { return roll_; }
private:
    std::string name_;
    int roll_;
};

int main() {
    Student s("Meena", 17);
    std::cout << s.roll() << ' ' << s.name() << '\n';   // 17 Meena
}
An object that cannot be built in a bad state removes many checks elsewhere.

Practice (0/4 done)

Task 1 · Question

Which is a correct constructor for class Box?

Task 2 · Question

What is the output? struct A { A() { std::cout << "a"; } ~A() { std::cout << "x"; } }; int main() { A one; A two; }

Task 3 · Question

class P { const int id_; public: P(int id) { id_ = id; } }; Why does this fail to compile?

Task 4 · Practice

Write a class Timer that prints "start" in its constructor with a label, and in its destructor prints the label and how many milliseconds passed. Use <chrono> (std::chrono::steady_clock). Create a Timer at the start of a block that runs a loop, and see the time printed when the block ends.

Quick quiz

Question 1

1.When does a local object's destructor run?

Question 2

2.You wrote Student(std::string name, int roll). What happens with Student s;?

Question 3

3.Why mark a one-argument constructor explicit?

Question 4

4.In what order are members initialised?

Question 5

5.What does Widget w(); declare?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

When does a local object's destructor run?

Answer: When the object goes out of scope

Local objects are destroyed automatically at the end of their scope.

You wrote Student(std::string name, int roll). What happens with Student s;?

Answer: Compile error: no default constructor exists

Writing any constructor stops the compiler from generating the default one.

Why mark a one-argument constructor explicit?

Answer: To stop silent implicit conversions from that argument type

explicit prevents surprises such as passing 42 where a Counter is expected.

In what order are members initialised?

Answer: The order they are declared in the class

Declaration order always wins; keep the initialiser list in the same order.

What does Widget w(); declare?

Answer: A function named w that returns a Widget

This is the "most vexing parse". Use Widget w; or Widget w{};.

Which pair makes a deep copy automatically when you copy the object?Related

Answer: A std::vector member

std::vector copies all its elements into new memory.

From Copying objects: the rule of three, five and zero

When does Java add a default no-argument constructor?Related

Answer: Only when the class declares no constructor at all

Writing any constructor removes the default one.

From Constructors and this

An exception is thrown inside a function. What happens to the local objects already created there?Related

Answer: Their destructors run as the exception leaves the scope (stack unwinding)

Stack unwinding destroys local objects in reverse order, which is what makes RAII reliable.

From RAII: let objects clean up for you

What does : this(name, 0m) after a constructor header do?Related

Answer: Calls another constructor of the same class first

It chains to another constructor to reuse its setup code.

From Classes, objects and constructors

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Classes, constructors and RAII” before its exam: RAII: let objects clean up for you, Copying objects: the rule of three, five and zero.
  2. Module 4 exam: Classes, constructors and RAII · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Operator overloading, inheritance and polymorphism. 42 topics left in the course before the final exam.

← Previous: Classes and objects: data plus behaviour