Skip to content

Module 3 — Constructors and Destructors

After this module you can predict exactly when an object comes into existence, becomes valid, and stops existing — and you can make every one of your objects valid from the moment it exists.


At a glance

Prerequisites Module 2
Estimated study time 7–9 hours
Lab Lab 3 — lifecycle counters, with predictions before running
Exercises Three tiers — warm-up, core, challenge
Cognitive levels L1 Understand → L5 Design — exit level L4 Analyze
Self-assessment Module quiz

Why this module matters

An object that exists in an invalid state is the source of an enormous number of bugs. A Book with no title, a Date with month 13, a container whose size does not match its storage — all of them are possible if you let a type be created carelessly, and all of them produce faults far away from where the mistake was made.

Constructors are how C++ says: there is no way to make one of these that is not valid. That is a design guarantee, not a convenience.

Destructors are the other half. Every object eventually stops existing, and the destructor is the single place that runs at that moment. Getting comfortable with this pairing is what makes Modules 10, 11 and 13 — copying, ownership, and exceptions — tractable instead of mysterious.

This module also contains the most valuable exercise in the early course: writing down what the constructors and destructors will say before you run the program. Almost everyone is wrong the first time. That gap is the lesson.


Learning objectives

By the end of this module you will be able to:

  1. [L2 Apply] Write a default constructor, a parameterised constructor, and a delegating constructor, and say when each is used.
  2. [L2 Apply] Use a member initialiser list, and explain why initialising a member is not the same as assigning to it in the constructor body.
  3. [L4 Analyze] Explain why initialisation follows the order members are declared, not the order they are written in the list — and diagnose the case where the two disagree.
  4. [L2 Apply] Use explicit, = default and = delete appropriately.
  5. [L4 Analyze] Explain what explicit prevents, and give a case where its absence is a real bug.
  6. [L2 Apply] Write a destructor, and state the rule for when a class needs one.
  7. [L3 Modify] Predict the order in which members and local objects are constructed and destroyed in a given program — before running it, and including an instrumented trace you have not seen.
  8. [L1 Understand] Explain what a temporary object is, how long it lives, and when binding it to a const& changes that.
  9. [L1 Understand] Explain how a constructor prevents a class from ever existing in an invalid state.
  10. [L1 Understand] Recognise the connection between destructors and resource release — the idea that Module 11 turns into RAII — and name what else a class needs when it owns a raw resource.

This module's exit level is 4 Analyze: you are finished when you can predict a program's object lifetimes before running it, and explain every difference in terms of the language when your prediction was wrong.


Core concepts

  • Constructor — a member function with the class's name and no return type, run automatically when an object is created.
  • Default constructor — callable with no arguments. Note that declaring any other constructor suppresses the implicitly generated one.
  • Parameterised constructor — takes arguments to establish the object's initial state.
  • Delegating constructor — one constructor calling another of the same class to avoid repeating initialisation logic (C++11).
  • Member initialiser list — the initialisation performed before the constructor body runs. The only way to initialise const members, reference members, and members with no default constructor.
  • Initialisation vs assignment — initialisation happens once, at creation; assignment happens later, to an object that already exists.
  • Declaration-order initialisation — members are initialised in the order they are declared in the class, whatever order the list uses. A real trap when one member's initialiser uses another.
  • explicit — stops a single-argument constructor from being used as an implicit conversion.
  • = default / = delete — ask for, or forbid, a compiler-generated special member function, in the most self-documenting way C++ offers.
  • std::initializer_list — lets a constructor accept {1, 2, 3} directly.
  • Destructor — ~ClassName(), run automatically when an object's lifetime ends.
  • Construction order — base class, then data members in declaration order, then the constructor body.
  • Destruction order — exactly the reverse.
  • Temporary — an unnamed object created during expression evaluation.
  • Invariant — a statement about an object that is always true. The constructor is where it is established; the public interface is where it is preserved.

Module contents

Section What it contains
Introduction (this page) Overview, prerequisites, objectives, concept inventory
Concepts and explanations What a constructor is for, default and overloaded constructors, the initialiser list, destruction order, temporaries, storage duration, initialisation versus assignment
Worked examples Four traces plus one analysis, each with a prediction prompt
Code examples examples/module-03/ — four programs, three with captured output
Misconceptions and mistakes Ten wrong mental models, eight real messages, including the Loan(bool) = delete trap
Debugging The lifecycle trace (predict, print, compare), four lifetime diagnoses, and the escalation ladder
Exercises Three tiers, each stating its level and its success criterion
Lab 3 Instrument the running system, and hand in a prediction written before the run
Challenge problems Value versus entity, owning classes, and the refactor that removes a hand-written destructor
Self-assessment Nine questions including two traces, with answers and a re-test schedule
Summary What must be retained, the terms to be able to define, and the checklist
Further reading Books and official documentation

What you can do after this module

  • Write a class where every path of construction produces a valid object.
  • Explain a program's object lifetimes to somebody else, in order.
  • Recognise a class that should have a destructor but does not.
  • Predict the output of a program full of constructor and destructor traces — the single best preparation for the resource-management modules.

Where this is used later

  • Invariants established here are enforced by the public interface in Module 4.
  • Destructors become resource release in Module 10 and Module 11.
  • Delegating and inheriting constructors reappear in Module 6, where a derived class must initialise its base.
  • Copy construction — the fourth special member function, deliberately not covered here — is the subject of Module 10.

Common mistakes to watch for

Symptom Cause
error: no matching function for call to 'Book::Book()' You declared a parameterised constructor, which suppressed the default one, and then default-constructed somewhere
A member is garbage, or a const member fails to initialise The member was assigned in the body instead of initialised in the initialiser list
error: uninitialized const member A const member must be initialised in the initialiser list; it cannot be assigned later
A member depends on another and gets nonsense Initialisation happens in declaration order, not list order
error: converting to 'Book' from initializer list would use explicit constructor Working as intended: explicit blocked an implicit conversion
An object seems to be destroyed "early" You passed it by value, so a temporary copy was made and destroyed. Discussed fully in Module 10

Skills checklist

Levels this module works at: [L1 Understand → L5 Design] — see How you will learn.

Tick an item only when the evidence exists:

  • [ ] I can write a class whose every constructor produces a valid object — evidence: exercise C1's Locker, with a const member and a reference member initialised in the list
  • [ ] I can predict an object's lifetime before running the program — evidence: Lab 3's prediction.md, with its difference table
  • [ ] I can explain every difference between a prediction and a trace in terms of the language — evidence: the mechanism named for each row of that table
  • [ ] I check the live counter and it returns to zero — evidence: your instrumented run, plus the one where it did not and you found the missing destruction
  • [ ] I can say whether a given class needs a destructor, and what else it needs if it does — evidence: the analysis in challenge X2
  • [ ] I can explain why the initialiser list is required, not preferred — evidence: exercise C3's two error messages, quoted
  • [ ] I can tell explicit refused a conversion before the compiler told me — evidence: exercise C5, part 3
  • [ ] I can remove a hand-written destructor and copy constructor and end up with a safer class — evidence: challenge X3

Suggested next step

Previous: Module 2 — Classes and Objects · Next: Module 4 — Encapsulation · All modules: Modules index