Multiple Inheritance & Virtual Inheritance

Course 3 · Ch 1
Multiple Inheritance & Virtual Inheritance
A capability C never had, and Rust deliberately never built — for good reason

Course 3 goes deeper into C++'s own mechanics, starting with something neither of this course's two comparison languages support in the same form.

Multiple Inheritance

class Derived : public Base1, public Base2 { // inherits members and methods from BOTH bases directly };

A class can inherit from more than one base class at once — something neither C's plain structs (c2-1, no inheritance concept at all) nor Rust's traits support in this exact form.

The Diamond Problem

The classic scenario: class D inherits from both B and C, and both B and C themselves inherit from a common base A. Without intervention, D ends up with two separate copies of A's data — one via each path — genuinely ambiguous which copy a reference to an inherited A member refers to, producing a real compile error unless explicitly disambiguated.

A Worked Example

class Animal { public: std::string name; }; class Swimmer : public Animal {}; class Flyer : public Animal {}; class Duck : public Swimmer, public Flyer {}; Duck d; d.name = "Donald"; // compile error — ambiguous: Swimmer::name or Flyer::name?

virtual Inheritance — The Fix

class Swimmer : virtual public Animal {}; class Flyer : virtual public Animal {}; // now Duck has exactly one shared Animal sub-object

With both paths declared virtual, Duck gets exactly one shared Animal sub-object, resolved correctly. Under the hood, a hidden pointer/offset table tracks the shared base's actual location — genuinely comparable in spirit to cpp1-7's own vtable: hidden compiler machinery solving an ambiguity problem.

Why This Is Genuinely Complex and Often Avoided

Honestly: multiple inheritance combined with virtual inheritance is widely considered one of C++'s more error-prone, rarely-actually-needed corners. Most style guides recommend avoiding multiple inheritance of implementation entirely, reserving it for multiple inheritance of pure interfaces — abstract classes containing only pure virtual functions (cpp1-7's own = 0 syntax) — which sidesteps the diamond problem entirely, since there's no actual data to duplicate.

Contrasted With Rust's Trait-Based Composition

Rust deliberately has no multiple inheritance of structs at all. A struct can implement any number of traits, and traits can have default method implementations — but a struct never inherits data from multiple sources the way a C++ class can. This sidesteps the diamond problem structurally, by design, rather than needing a virtual-inheritance fix bolted on afterward. Rust's own answer to "I need behavior from multiple sources" is composition and multiple trait implementation, not multiple inheritance of state.

ConceptC++ Multiple InheritanceRust Traits
Inheriting data from multiple sourcespossible — the diamond problem's real causenot possible at all — structurally avoided
Inheriting behavior from multiple sourcespossible — via multiple abstract interfacespossible — implement any number of traits
Fix for ambiguous shared basesvirtual inheritance — a real, non-trivial mechanismn/a — the ambiguity was never possible to create
Prefer multiple inheritance of pure interfaces
Multiple inheritance of all-pure-virtual base classes sidesteps the diamond problem entirely in the vast majority of legitimate real-world use cases — reserve multiple inheritance of actual implementation for the rare cases that genuinely need it.
Both inheritance paths must agree on virtual — one alone isn't enough
Marking only one of the two paths to a shared base as virtual, but not the other, produces inconsistent, confusing behavior. Both paths need to agree on using virtual inheritance for the fix to actually work.

Coding Challenges

Challenge 1

Reproduce this chapter's Animal/Swimmer/Flyer/Duck diamond WITHOUT virtual inheritance, and report the exact compile error produced when trying to access d.name.

📄 View solution
Challenge 2

Fix Challenge 1 by adding virtual to both Swimmer's and Flyer's inheritance from Animal, then successfully set and print d.name.

📄 View solution
Challenge 3

Explain precisely why the diamond problem cannot occur in Rust at all, tying your answer to the specific structural difference between how a C++ class inherits from a base and how a Rust struct implements a trait.

📄 View solution

Chapter 1 Quick Reference

  • class D : public B1, public B2 {} — multiple inheritance, unavailable in C or Rust in this form
  • Diamond problem — a shared base reached through two paths produces two ambiguous data copies
  • virtual public Base on both paths — the fix, ensuring exactly one shared sub-object
  • Prefer multiple inheritance of pure abstract interfaces over multiple inheritance of implementation
  • Rust avoids this entirely — traits provide behavior, never inherited data, so the ambiguity can't arise
  • Next chapter: templates, deeper — specialization, variadic templates, and a light touch on C++20 concepts