Skip to content

Module 3 — Summary

The essentials

  • An object is built in a fixed order: base class, then members in declaration order, then the constructor body. Destruction is the exact reverse. Nothing can reorder that, and the initialiser list cannot pretend to.
  • A constructor's job is to make an invalid object impossible — not to save typing. If every path into a class establishes the invariant, then every later function may assume it, and the assumption is safe.
  • Declaring any constructor suppresses the compiler's default constructor. So does a constructor whose every parameter has a default. = default asks for the generated one back; = delete refuses an operation explicitly, with a message that says what you meant.
  • The member initialiser list constructs; the body assigns. They are different operations with different costs, and for const members, reference members and members without a default constructor the body is not an option at all. Errors: uninitialized const member, uninitialized reference member, no matching function for call to 'Slot::Slot()'.
  • Members are initialised in declaration order, whatever order the list uses, and the compiler warns (-Wreorder) when the two disagree. Write the list in declaration order.
  • explicit on a single-argument constructor prevents silent conversions. Without it, Loan loan = 14; and take("alice") can both compile when you did not mean them to.
  • A delegating constructor hands its work to another, which runs first and completely. Your body then runs on a finished object.
  • A destructor runs automatically when an object's lifetime ends, and a class needs one only when it owns a resource the generated destructor cannot release. Writing one for every class adds nothing; omitting one where it is needed leaks.
  • A class that owns a raw resource needs a destructor, a copy constructor and a copy assignment operator. The generated copies copy the handle, so two objects end up owning one thing. Getting all three right by hand is Module 10; the escape route is Module 11.
  • A by-value parameter is a copy, made before the function body runs and destroyed when the function returns. Count them: programs contain more objects than they appear to.
  • A temporary lives to the end of its full expression, unless it is bound to a const& (or &&), which extends its life to the reference's.
  • Under C++17, a returned prvalue is constructed directly in the caller — one construction and no temporary. Under C++14 that is permitted but not guaranteed, which is why every command in this course pins -std=c++17.
  • Initialisation is not assignment: a constructor versus operator=, an object that does not exist versus one that does. The difference becomes a double free in Module 10.

The one-sentence version

Every object's life is a sequence of well-defined moments — construction of members, construction of the whole, then destruction in reverse — and a program whose author can predict that sequence can reason about ownership, copying and resources.

Terms you must now be able to define

Constructor · default constructor · parameterised constructor · constructor overloading · member initialiser list · initialisation versus assignment · declaration order · explicit · = default · = delete · delegating constructor · destructor · object lifetime · automatic (stack) storage · static storage · temporary · full expression · lifetime extension · copy elision · trivial constructor · aggregate

See the glossary. Three are load-bearing for what follows: temporary, copy elision and the constructor/destructor pairing. Module 10 assumes all three.

The habit to keep

Predict the lifetime before you instrument it, and check that the live counter returns to zero. Every resource bug in Modules 10 and 11 is, at bottom, a miscounted lifetime. The habit of predicting one is cheaper than any debugger.

Before you move on

  • [ ] All warm-up and core exercises attempted, with results checked against the module's own captured outputs
  • [ ] Lab 3's prediction written before running, with a difference table that names a mechanism for every line that changed
  • [ ] The live counter for every instrumented class returns to 0, and you can say why
  • [ ] Your clean Lab 2 program still produces byte-identical output
  • [ ] At least one challenge problem attempted, with its rubric applied honestly
  • [ ] Self-assessment ≥ 7, or the misses re-answered — in particular question 3, whose trace you should be able to produce for any small program
  • [ ] The skills checklist on the module page ticked with evidence
  • [ ] You can look at a class and say, without running it, whether it needs a destructor

Further reading

  • C++ Primer (5th ed.), chapters 7.1–7.5 (constructors, the initialiser list, delegation) and 13.1 (the special member functions, as a preview). The initialiser-list section is the one this module's §4 is built on.
  • Effective C++ (3rd ed.), Items 4, 9–12 — "make sure objects are initialised before they are used", and why copy constructors and destructors come as a set. Written by someone who spent years debugging exactly what this module teaches.
  • Effective Modern C++, Items 41–42 — on copy elision, pass-by-value and the guarantee C++17 makes mandatory. Read Item 41 after this module's §8, not before.
  • A Tour of C++ (3rd ed.), chapter 4 — constructors, destructors and the initialiser-list rule in a compact form, useful as a second pass.
  • The C++ Core Guidelines — C.20–C.22, C.41 (a constructor should create a fully initialised object), C.43 and R.11 (avoid explicit new). Six short rules that anticipate Modules 10 and 11.

See Further Reading for editions and why these are the right ones.


Next: Module 4 — Encapsulation. Module 4 takes the invariant this module established — "an object cannot exist in a bad state" — and makes it the organising idea of a whole class design, including what happens when a caller tries to break it deliberately.