Module 8 — Summary¶
The essentials¶
= 0makes a class abstract, and that is a statement rather than an omission. No object of the general type exists, so there is no way to write one — and the compiler'snote:lines list exactly which contracts are still unimplemented.- A definition does not make a pure virtual function non-pure.
= 0decides abstractness and the obligation to override; a body only decides what a subclass may call by qualification. The one case where the definition is mandatory is a pure virtual destructor, because it always runs — and its absence is a link error, which on some toolchains names no symbol at all. - Two shapes of abstraction, one rule. An abstract base class carries state and a partial algorithm, with protected pure virtual extension points; an interface has no data and only pure virtual functions. Interfaces can be inherited several times safely; stateful bases cannot, because that is Module 6's diamond.
- A non-virtual public function that calls protected virtuals is the strongest form. The base owns the algorithm and guarantees its shape; the subclass supplies only the parts that genuinely vary. Making that function virtual gives away a guarantee you wanted.
- An interface must be small, and the test is honesty: can every implementation honour
every function without lying? A wide interface does not remove change, it relocates it to
implementers — who are more numerous, and unknown. A function that throws or returns
falsefor "unsupported" is a lie the type system permits. - Failure belongs in the contract. A returned value type (
PaymentResult) carries a reason and can be printed; a barefalsecannot. Module 13 is where exceptions become the right answer, and it is a decision, not a default. - Abstract types have no value semantics. Everything lives behind a reference or pointer, so every collection of them raises an ownership question — and that question is handed to Module 11 rather than solved here.
- Depth costs assumptions per level. An interface hierarchy adds one level and stops; a stateful hierarchy invites more. If "is-a" needs a qualification to be true, the hierarchy is one level too deep — and the repair is usually an interface or a composed strategy object, not another level.
The one-sentence version¶
Make the varying part pure virtual so the compiler enforces the contract, keep the interface small enough that every implementation can honour it honestly, let a non-virtual base function own the algorithm, and remember that an abstract type buys extensibility with value semantics and draws you an ownership question.
Terms you must now be able to define¶
Pure virtual function · abstract class · concrete class · interface · abstract base class · design by contract · precondition · postcondition · invariant · non-virtual interface (NVI) · pure virtual destructor (and why it needs a definition) · protected non-virtual destructor · interface segregation · wide interface · fragile base class · covariant return type · dependency inversion (preview) · template-method pattern (preview)
See the glossary. Three matter most for what follows: interface segregation and dependency inversion are the SOLID principles of Module 15, and NVI becomes the template-method pattern there. The contracts you wrote here are what the capstone (Module 16) is implemented against.
The habit to keep¶
Audit the interface before you debug the implementation. Two columns, one row per
function: can every implementation honour this without lying? The first "no" is a bug in the
interface, and everything downstream of it — a throwing override, a false that means
"unsupported", a dynamic_cast at a call site — is a symptom. Ten minutes, and it usually
costs less than reading one failing implementation.
Second habit: write the four contract lines before the function. Precondition, postcondition on success, postcondition on failure, invariant. A contract whose failure case has never been run is not a contract; it is a description of the happy path.
Before you move on¶
- [ ] Warm-up and core exercises attempted, with results checked
- [ ] Lab 8 builds with no warnings, and its contract audit changed at least one decision
- [ ] You can write an abstract base class and an interface from memory, and say why you chose one over the other
- [ ] You have seen a link error caused by a pure virtual destructor, and recorded what it looked like on this toolchain
- [ ]
Item item;on your Lab 8 interface still fails to compile, and you can explain why that is good - [ ] No function in your interfaces is implemented by a class that cannot honour it
- [ ] 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 and 35 — the interface/implementation split that
interfaces formalise, and the alternatives to virtual functions, including the
non-virtual interface idiom this module uses in
describe()andrender(). - Design Patterns (GoF), "Program to an interface, not an implementation", and the Template Method and Strategy patterns. Template Method is the NVI shape — a non-virtual public function calling protected virtual steps — and Strategy is the policy object your Lab 8 injects.
- The C++ Programming Language (4th ed.), the chapter on abstract classes — the language's own account of pure virtual functions, interface inheritance versus implementation inheritance, and why an abstract base always needs a virtual destructor.
- The C++ Standard, [class.abstract] and [class.dtor] — abstractness, and the rule that a destructor runs for every complete object. Two short clauses that between them explain why a pure virtual destructor needs a definition.
- C++ Core Guidelines —
C.35(a base destructor is public and virtual, or protected and non-virtual),C.126–C.129(abstract classes and interfaces; use= 0for interfaces),I.25(prefer abstract classes as interfaces) andI.24/I.26(avoid making a class abstract, or derived from one, "just in case"). - Clean Architecture (Martin), the discussion of boundaries and dependency inversion — where "depend on abstractions, not concretions" is explained from the dependency direction, which is the conceptual step Module 15 formalises.
See Further Reading for editions and why these are the right ones.
Next: Module 9 — Composition and Association. Modules 6–8 built families of types that are kinds of one another. Module 9 turns to the other half of object-oriented design — types that are made of other types, that own them, that merely know about them, and that must survive a header cycle when two of them refer to each other.