Module 7 — Summary¶
The essentials¶
- The static type of the expression decides the call unless the function is
virtualand the call is made through a reference or pointer. A call on a named object is always statically bound — even when the function is virtual — because the compiler knows the exact type. virtualis opt-in, per function, and it decides the behaviour of every loop written later. The choice is made once, in the base class, and it is invisible at the call site:1200000where780000was expected, with no diagnostic anywhere.- Any base class that may be destroyed through a base pointer needs a virtual
destructor, written as
virtual ~Base() = default;. Without one, only the base destructor runs and the standard makes it undefined behaviour — so the symptom is not a message you can learn to recognise, which is why the rule is preventive. - A copy constructs an object of the static type. By-value parameters, containers of
objects, and
Base b = derived;all slice. Polymorphic parameters and containers take handles:const Base&,Base*,std::unique_ptr<Base>. - A virtual call during construction or destruction resolves to the class being constructed or destroyed, not the most-derived type. Compliance is silent; if the function is pure virtual, the call is undefined behaviour.
overridemakes the compiler check your claim;finalrefuses a claim someone else might make. Both are free, and both convert silent mistakes into build failures. An override replaces the base implementation — to keep the base's work, call it by qualification.- Polymorphism costs one hidden pointer per object and one indirection per call. The
program measures both:
sizeof(Shape) = 8,sizeof(Circle) = 16, against a tagged struct's 24 bytes. The benefit is not speed — it is that adding a kind stops being an edit in four places that must agree. - A
switchon a type tag and a hierarchy are two ways of writing the same decision. The refactor between them changes nothing about the output and everything about the cost of the next change.-Wswitchprotects the switches that name the enum; it does not protect the design.
The one-sentence version¶
Declare virtual where a subclass may legitimately change the answer, always give a
polymorphic base a virtual destructor, pass polymorphic objects only by reference or
pointer, and never call a virtual function from a constructor or destructor.
Terms you must now be able to define¶
Polymorphism · virtual function · override · override and final · static binding ·
dynamic binding · dispatch · vtable / vptr (as a typical implementation) · virtual
destructor · object slicing · polymorphic value semantics · pure virtual function
(preview) · abstract class (preview) · dispatch during construction and destruction
See the glossary. Three matter most for what follows: pure virtual function and abstract class are Module 8's entire subject, and ownership of polymorphic objects is what Modules 10 and 11 exist to make safe.
The habit to keep¶
Run the declared-type test on every call site you find surprising. Write one line:
static type of the expression, virtual yes/no, handle form (reference, pointer, or
object). Two minutes, and it resolves the vast majority of dispatch bugs — while the
third of the three conditions explains why a call in a constructor cannot dispatch and a
call on a named object does not need to.
Second habit, once per hierarchy: grep for the type name with a space or a < after
it. void f(Person, std::vector<Person>, Person person = — every hit is a slice,
and the compiler will not tell you about any of them.
Before you move on¶
- [ ] Warm-up and core exercises attempted, with results checked
- [ ] Lab 7 builds with no warnings, keeps the recorded failure, and its leak counter returns to zero
- [ ] You can write the declared-type test for a call site in under a minute
- [ ] You have reproduced a slice on purpose and fixed it with handles
- [ ] You can explain the four-answer trace without looking at the program
- [ ] You can name three places
virtualwould be the wrong choice, with a reason each - [ ] Self-assessment ≥ 80%, with question 10's four defects all found
- [ ] At least one challenge problem attempted, with the rubric applied
- [ ] The skills checklist on the module page ticked with evidence
Further reading¶
- Effective C++ (3rd ed.), Items 34–36 and 40 — the interface/implementation split, virtual functions and the alternatives to them, never redefining an inherited non-virtual function, and why multiple inheritance should be interface-only. Item 34's "alternative to virtual functions" list is the honest counterweight to this module's enthusiasm.
- *Effective Modern C++, Items 17 and 21 — virtual destructors, and the
std::unique_ptrthat makes polymorphic ownership routine. Read 21 with Module 11. - The C++ Programming Language (4th ed.), the chapter on class hierarchies — the language's own account of abstract classes, dispatch and the areas where virtual functions cost something.
- The C++ Standard, [class.virtual] and [expr.call] — dispatching, overriding and the rule about calling virtual functions during construction and destruction, in the normative wording. Nine paragraphs, and the source of every claim above.
- Design Patterns (GoF), the introduction's section on dynamic binding, and the Strategy and Template Method patterns — the shapes that virtual functions make possible and Module 15 names.
- C++ Core Guidelines —
C.35(a base class destructor should be either public and virtual or protected and non-virtual),C.67/C.146(don't slice; store and pass polymorphic objects by pointer or reference), andC.128(virtual,overrideandfinal— use exactly one of them per declaration).
See Further Reading for editions and why these are the right ones.
Next: Module 8 — Abstract Classes. Module 8 takes the hierarchy you can now dispatch and turns the base into a contract: a class that cannot be instantiated, whose pure virtual functions every concrete type must implement. It is also where the running system finally loses the design smell this module kept pointing at — a base field that means nothing for some of its derived classes.