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:
- [L2 Apply] Write a default constructor, a parameterised constructor, and a delegating constructor, and say when each is used.
- [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.
- [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.
- [L2 Apply] Use
explicit,= defaultand= deleteappropriately. - [L4 Analyze] Explain what
explicitprevents, and give a case where its absence is a real bug. - [L2 Apply] Write a destructor, and state the rule for when a class needs one.
- [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.
- [L1 Understand] Explain what a temporary object is, how long it lives, and when
binding it to a
const&changes that. - [L1 Understand] Explain how a constructor prevents a class from ever existing in an invalid state.
- [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
constmembers, 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 aconstmember 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
explicitrefused 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