Module 6 — Inheritance¶
After this module you can build and reason about a class hierarchy, including multiple inheritance and the diamond, and you can tell when inheritance is the wrong tool for the job.
At a glance¶
| Prerequisites | Module 5 |
| Estimated study time | 8–10 hours |
| Lab | Lab 6 — a family of lendable items |
| Exercises | Three tiers — warm-up, core, challenge |
| Cognitive levels | L3 Modify → L5 Design — exit level L5 Design |
| Self-assessment | Module quiz |
Why this module matters¶
Inheritance is the feature that most visibly separates object-oriented code from procedural code — and the one most often used where it should not be. Both halves of that sentence are the content of this module.
Used well, inheritance expresses a genuine is-a relationship and lets you write shared behaviour once. The running system of this course, for example, has several kinds of lendable item that share an identifier, a title and an availability flag. Writing those three things once in a base class, instead of three times, is real value.
Used badly, inheritance binds two types together so tightly that changing either one breaks the other. The classic signs: a derived type that ignores most of the behaviour it inherited, or a hierarchy that grows a new level every time a feature is added.
You will learn both, including the concepts that make the difference precise: construction order, access modes, name hiding, and the diamond problem. At the end you will replace an inheritance design with composition and justify the choice in writing — a decision you will make for real in Module 9.
Learning objectives¶
By the end of this module you will be able to:
- Declare a derived class and explain the is-a relationship it claims.
- Use
public,protectedandprivateinheritance, and state precisely which members remain accessible at which access level in each case. - Explain the order of construction and destruction across a hierarchy: base, then members, then derived body — and the exact reverse on destruction.
- Initialise a base class from a derived class's member initialiser list, and explain why a base must be constructed before derived members.
- Use
protectedaccess and articulate why it is a strong coupling decision rather than a convenience. - Explain name hiding: a derived member with the same name hides the base's, and how to reach the base version deliberately.
- Distinguish single, multilevel, multiple and hierarchical inheritance, and describe a realistic use for each.
- Explain the diamond problem, why it arises with multiple inheritance, and how virtual inheritance resolves ambiguity in the one shared base subobject.
- Use
using Base::Base;to inherit constructors. - Mark a class
finalwhere further derivation would be unsafe or meaningless. - Decide, with reasons, whether a given requirement calls for inheritance or composition.
Core concepts¶
- Inheritance — defining a new class in terms of an existing one.
- Base and derived — the general class and the specialised one.
is-a— the test that justifies inheritance. A book is a lendable item; a library has books.has-ais composition (Module 9).- Access modes —
publicinheritance preserves the base's access levels;protectedandprivateinheritance reduce the base's public members toprotectedorprivatein the derived class, and both restrict who can use the inheritance relationship at all. protectedmembers — accessible to the class itself and to derived classes, but not to outside callers.- Base initialisation — a derived constructor must call a base constructor; if the base has no default constructor, it must be listed in the derived member initialiser list.
- Construction order — base → derived data members in declaration order → derived constructor body.
- Destruction order — derived body → derived members → base. The reverse, always.
- Name hiding — a derived-class member name hides all base-class members of
that name; qualified calls (
Base::f()) reach the hidden one. - Multiple inheritance — deriving from more than one base.
- The diamond problem — two bases inheriting from a common ancestor give a class two copies of that ancestor's members, so an inherited name becomes ambiguous.
- Virtual inheritance —
virtualbefore the base name, so a diamond shares one subobject of the common base instead of duplicating it. - Inheriting constructors —
using Base::Base;makes base constructors available in the derived class. final— a class markedfinalcannot be derived from.- Fragile base class — the base's implementation leaks into derived classes
through
protectedmembers, so changing the base silently breaks derived code. - Slicing — assigning a derived object into a base object copies only the base part. Where it hurts is the subject of Module 7.
Module contents¶
| Section | What it contains |
|---|---|
| Introduction (this page) | Overview, prerequisites, objectives, concept inventory |
| Concepts and explanations | Base and derived classes, access modes, construction and destruction order, name hiding, multiple inheritance and the diamond, composition over inheritance |
| Worked examples | Six programs — a payroll hierarchy, the three inheritance modes side by side, a lifecycle trace, three hierarchy defects, an attack on an inherited invariant, and using Base::Base; |
| Code examples | examples/module-06/ — six programs, each with captured output that the build verifies |
| Misconceptions and mistakes | Seven wrong mental models, ten symptoms, and six worked mistakes with their real compiler messages |
| Debugging | The subobject map, the three silent failures to rule out first, and a worked diagnosis |
| Exercises | Three tiers, including four broken hierarchies to predict before compiling and an audit of someone else's |
| Lab 6 | The catalogue grows a family of items — with a construction trace predicted before the first run |
| Challenge problems | The flat design that might be right, a refactor of a hierarchy you did not write, and an argument for depth |
| Self-assessment | Ten questions, including two predictions and a classification task |
| 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¶
- Move behaviour that is genuinely common into a base class without duplicating it.
- Trace the exact order of constructor and destructor calls in a hierarchy and predict the output before running.
- Diagnose an ambiguous member access and fix it correctly.
- Recognise a diamond and apply virtual inheritance — or redesign to avoid it.
- Reject an unnecessary hierarchy and explain what composition would do better.
Where this is used later¶
virtualfunctions — how a hierarchy becomes polymorphic rather than merely shared — are the whole of Module 7.- Abstract base classes turn a hierarchy into an interface in Module 8.
- Virtual destructors, mentioned here, become essential in Module 7.
- Composition vs inheritance is examined properly in Module 9, and again as the open/closed principle in Module 15.
- Slicing is reproduced deliberately in Module 7.
Common mistakes to watch for¶
| Symptom | Cause |
|---|---|
error: 'Base' is an inaccessible base of 'Derived' |
The inheritance was written without public — for a class, inheritance defaults to private |
error: no matching function for call to 'Derived::Derived()' |
The base has no default constructor and the derived constructor does not mention it in the initialiser list |
error: 'display' is a private member of 'Base' |
Calling a private or protected base member from outside the hierarchy |
| A base member "disappears" in the derived class | Name hiding. Qualify the call as Base::member() to reach it |
error: reference to 'name' is ambiguous |
The diamond: two inherited copies of the same member |
| Something is destroyed twice, or not at all | A missing virtual destructor on a polymorphic base — see Module 7 |
| A derived object's extra data is silently lost | Object slicing — assigning a derived object to a base object by value. See Module 7 |
| A change to the base breaks many derived classes | The fragile base class problem: the base's implementation leaked through protected members |
Skills checklist¶
Levels this module works at: [L3 Modify → L5 Design] — see How you will learn.
Tick an item only when the evidence exists:
- [ ] I can build a hierarchy in which the base holds only what is genuinely shared —
evidence: your Lab 6 base, with the sentence for every
protectedmember - [ ] I can predict the construction and destruction order of a three-level object before running it — evidence: a written trace and the real one, side by side
- [ ] I can say what each inheritance mode changes and what it does not —
evidence: C2's four predictions, including the two
classdefaults - [ ] I can diagnose a hidden overload from the compiler's message and fix it at the
design rather than the call site — evidence: the
using Base::print;line and the call sites it repaired - [ ] I can count the base subobjects of a multiply-inherited class and say what a
second copy costs — evidence: your
TeachingAssistantexperiment - [ ] I can audit a base class's public operations and name the one that decides against inheritance — evidence: C4's three-column audit
- [ ] I can replace an inheritance relationship with composition and say what the callers gained — evidence: the Lab 6 composition note and X2's before/after
Suggested next step¶
Previous: Module 5 — Operators and Static Members · Next: Module 7 — Polymorphism · All modules: Modules index
Module 7 assumes your hierarchies are honest — every derived class is genuinely its base, for every operation the base publishes. What it adds is the thing Example 1 of this module could only point at: a call through a base reference that runs the derived behaviour, and the three traps that come with it.