Skip to content

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:

  1. Declare a derived class and explain the is-a relationship it claims.
  2. Use public, protected and private inheritance, and state precisely which members remain accessible at which access level in each case.
  3. Explain the order of construction and destruction across a hierarchy: base, then members, then derived body — and the exact reverse on destruction.
  4. Initialise a base class from a derived class's member initialiser list, and explain why a base must be constructed before derived members.
  5. Use protected access and articulate why it is a strong coupling decision rather than a convenience.
  6. Explain name hiding: a derived member with the same name hides the base's, and how to reach the base version deliberately.
  7. Distinguish single, multilevel, multiple and hierarchical inheritance, and describe a realistic use for each.
  8. Explain the diamond problem, why it arises with multiple inheritance, and how virtual inheritance resolves ambiguity in the one shared base subobject.
  9. Use using Base::Base; to inherit constructors.
  10. Mark a class final where further derivation would be unsafe or meaningless.
  11. 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-a is composition (Module 9).
  • Access modes — public inheritance preserves the base's access levels; protected and private inheritance reduce the base's public members to protected or private in the derived class, and both restrict who can use the inheritance relationship at all.
  • protected members — 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 — virtual before 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 marked final cannot be derived from.
  • Fragile base class — the base's implementation leaks into derived classes through protected members, 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

  • virtual functions — 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 protected member
  • [ ] 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 class defaults
  • [ ] 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 TeachingAssistant experiment
  • [ ] 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.