Multiple Inheritance

Inherit from more than one base class at once — how the mechanics, ambiguity, and the diamond problem work, and when virtual inheritance or composition is the better tool.

Multiple inheritance lets a class derive from more than one base class simultaneously, combining their interfaces and implementations into one derived type. It is a powerful but sharp-edged tool: it introduces name conflicts, ambiguous base subobjects, and the diamond problem, each with a specific C++ rule for resolving it. This topic covers the mechanics precisely, then when multiple inheritance is genuinely worth its complexity versus when composition or single-inheritance interfaces serve better.

What Is Multiple Inheritance?

Single inheritance gives a derived class exactly one direct base class. Multiple inheritance allows a derived class to list more than one direct base class, inheriting members from each of them.

Car inherits from both Engine and GPS, gaining both start() and locate().

A Car object conceptually contains an Engine subobject and a GPS subobject as its base parts, plus whatever Car itself adds.

class Engine
{
public:
    void start() { std::cout << "engine starting\n"; }
};

class GPS
{
public:
    void locate() { std::cout << "finding position\n"; }
};

class Car : public Engine, public GPS
{
};

Basic Syntax for Multiple Base Classes

Each base is listed separated by commas, each with its own access specifier (public, protected, or private inheritance, exactly as for single inheritance).

There is no fixed limit on the number of base classes a derived class may list, though hierarchies with many unrelated bases are usually a sign the design should be reconsidered.

class Derived : public Base1, public Base2, public Base3
{
    // Derived's own members
};

Member Access With Multiple Bases

Publicly inherited members from each base are accessible on the derived object as if they were declared directly on it, as long as no name conflict exists.

Member access follows the same public/protected/private rules as single inheritance, applied independently to each base.

class Car : public Engine, public GPS
{
public:
    void status()
    {
        start();  // from Engine
        locate(); // from GPS
    }
};

Car car;
car.start();
car.locate();

Constructor Order With Multiple Bases

Base class subobjects are constructed in the order they are listed in the derived class's base-class list — not the order they appear in the member-initializer list. Constructing the derived class's own members happens after all base subobjects are constructed, in the order the members are declared.

Prints Engine(), then GPS(), then Car() — matching the base-list order (Engine, then GPS), regardless of how a constructor's initializer list might be written.

Engine is listed first in Car's base-class list, so its subobject is constructed first

GPS is listed second, so it is constructed next Car's own constructor body runs last, after both base subobjects exist

class Engine
{
public:
    Engine() { std::cout << "Engine()\n"; }
};

class GPS
{
public:
    GPS() { std::cout << "GPS()\n"; }
};

class Car : public Engine, public GPS
{
public:
    Car() { std::cout << "Car()\n"; }
};

Car car;

Destructor Order With Multiple Bases

Destruction always runs in exactly the reverse order of construction: derived class first, then bases in reverse of their base-list order.

This mirrors single inheritance's construction/destruction ordering rule, simply applied across every base in the list.

class Engine
{
public:
    ~Engine() { std::cout << "~Engine()\n"; }
};

class GPS
{
public:
    ~GPS() { std::cout << "~GPS()\n"; }
};

class Car : public Engine, public GPS
{
public:
    ~Car() { std::cout << "~Car()\n"; }
};

{
    Car car;
} // prints ~Car(), then ~GPS(), then ~Engine()

Name Conflicts Between Base Classes

If two base classes declare members with the same name, referring to that name through the derived class becomes ambiguous — the compiler cannot decide which base's member you mean without more information.

Both Left and Right contribute a report() member; Combined does not automatically prefer one over the other.

This ambiguity exists purely because of the name collision — it has nothing to do with the two functions' signatures matching or differing.

class Left
{
public:
    void report() { std::cout << "Left::report\n"; }
};

class Right
{
public:
    void report() { std::cout << "Right::report\n"; }
};

class Combined : public Left, public Right
{
};

Combined c;
// c.report(); // error: ambiguous — Left::report or Right::report?

Explicit Qualification to Resolve Name Conflicts

Qualifying the call with the base class name tells the compiler exactly which base's member to use, resolving the ambiguity.

A derived class can also resolve the conflict once, by defining its own member of that name that explicitly calls whichever base version(s) it needs.

Explicit qualification (Base::member) is the general tool for naming a specific base's member whenever the derived class's ordinary unqualified name would be ambiguous or would refer to the wrong base.

c.Left::report();
c.Right::report();
class Combined : public Left, public Right
{
public:
    void report() // Combined provides its own, unambiguous report()
    {
        Left::report();
        Right::report();
    }
};

Ambiguous Base Class Access

Ambiguity is not limited to member functions — it applies to data members and even to implicit conversions to a base type, whenever more than one base could equally satisfy the reference.

Even simple data members require explicit qualification once the same name exists in more than one direct base.

This is a direct, mechanical consequence of listing multiple bases that happen to share a member name — C++ does not attempt to guess intent.

class Left { public: int value = 1; };
class Right { public: int value = 2; };
class Combined : public Left, public Right {};

Combined c;
// int v = c.value; // error: ambiguous — Left::value or Right::value?
int v = c.Left::value; // fine: explicitly qualified

The Diamond Problem

The diamond problem arises when two base classes (say Engine and Battery) both derive from a common base (Vehicle), and a further class (HybridCar) derives from both Engine and Battery. Without special handling, HybridCar ends up containing two separate Vehicle subobjects — one through each path — which is usually not what the design intends.

The diamond shape: two inheritance paths from Vehicle converge again at HybridCar.

Without virtual inheritance, HybridCar contains two independent Vehicle subobjects — one via Engine, one via Battery.

Vehicle
         /    \
    Engine    Battery
         \    /
       HybridCar
class Vehicle
{
public:
    int weight = 0;
};

class Engine : public Vehicle {};
class Battery : public Vehicle {};

class HybridCar : public Engine, public Battery {};

Duplicate Base Subobjects Without Virtual Inheritance

car.weight is ambiguous because two distinct Vehicle subobjects each contribute their own weight. Setting one does not affect the other — they are genuinely separate objects in memory.

car contains an Engine subobject, which contains its own Vehicle subobject car also contains a Battery subobject, which contains a second, unrelated Vehicle subobject car.Engine::weight and car.Battery::weight refer to two different int objects output: 5 10 — the two values are independent

This duplication is often not what a hybrid-car design intends — usually there should be exactly one shared notion of the vehicle's weight, not two independently-tracked copies.

HybridCar car;
// car.weight = 5; // error: ambiguous — Engine::Vehicle::weight or Battery::Vehicle::weight?
car.Engine::weight = 5;
car.Battery::weight = 10;
std::cout << car.Engine::weight << ' ' << car.Battery::weight;

Virtual Inheritance Syntax

Declaring a base class as virtual tells the compiler that this base should be shared, so that any class ultimately inheriting from it through multiple paths gets exactly one subobject of it, no matter how many paths lead to it.

virtual on both Engine's and Battery's inheritance from Vehicle ensures HybridCar contains a single, shared Vehicle subobject.

Both intermediate classes (Engine and Battery here) must declare their inheritance from the shared base as virtual for the sharing to take effect at HybridCar.

class Vehicle
{
public:
    int weight = 0;
};

class Engine : public virtual Vehicle {};
class Battery : public virtual Vehicle {};

class HybridCar : public Engine, public Battery {};

How Virtual Inheritance Solves the Diamond Problem

With virtual inheritance, weight is no longer ambiguous, because there is only one Vehicle subobject shared by both paths.

Engine and Battery both inherit from Vehicle virtually HybridCar therefore contains exactly one Vehicle subobject, shared by both Engine and Battery car.weight unambiguously refers to that single shared object

Virtual inheritance collapses the two would-be-duplicate base subobjects into a single shared one.

HybridCar car;
car.weight = 5; // unambiguous: only one Vehicle subobject exists
std::cout << car.weight;
Without virtual inheritance:        With virtual inheritance:

   Engine --- Vehicle(A)                Engine --\
   Battery --- Vehicle(B)                          >-- Vehicle (shared, one copy)
   (two independent Vehicles)           Battery --/

Virtual Base Class Initialization Rules

A virtual base subobject is initialized directly by the most-derived class's constructor, not by the intermediate classes that virtually inherit from it — even though those intermediate classes may also list it in their own member-initializer lists.

Because HybridCar is the most-derived class, its own Vehicle(500) initializer is what actually constructs the shared Vehicle subobject; Engine's and Battery's own Vehicle(...) initializers are bypassed for that shared subobject.

This rule exists precisely because there is only one shared Vehicle subobject — it can only be initialized once, and the standard makes the most-derived class responsible for that single initialization, regardless of how deep the intermediate hierarchy is.

class Vehicle
{
public:
    explicit Vehicle(int w) : weight(w) {}
    int weight;
};

class Engine : public virtual Vehicle
{
public:
    Engine() : Vehicle(100) {} // ignored when Engine is not the most-derived class
};

class Battery : public virtual Vehicle
{
public:
    Battery() : Vehicle(200) {} // also ignored in that case
};

class HybridCar : public Engine, public Battery
{
public:
    HybridCar() : Vehicle(500), Engine(), Battery() {} // this Vehicle(500) is the one that runs
};

Constructor Order With Virtual Inheritance

Virtual base subobjects are constructed before any non-virtual base subobjects, in the order they first appear in a depth-first traversal of the base-class hierarchy — regardless of where they are listed in the derived class's own base-class list.

The shared virtual base is always constructed first, ahead of the ordinary (non-virtual) direct bases that depend on it.

Destruction, as with all C++ construction/destruction pairing, runs in the exact reverse order: HybridCar, then Battery, then Engine, then Vehicle.

Construction order for HybridCar : public Engine, public Battery
(with Engine, Battery each: public virtual Vehicle)

1. Vehicle       (virtual base, constructed once, first)
2. Engine         (non-virtual direct base, in listed order)
3. Battery        (non-virtual direct base, in listed order)
4. HybridCar       (the derived class itself, last)

Casting and Multiple Inheritance

Upcasting a multiply-inherited derived object to one of its base types still works as expected — a HybridCar* converts to an Engine* or a Battery* implicitly, and to a Vehicle* as well (unambiguously, if Vehicle is a shared virtual base, or explicitly qualified otherwise).

Each pointer refers to the appropriate subobject within the same HybridCar object.

As an implementation detail (not something a program should depend on), converting between these pointer types can require the compiler to adjust the numeric address value, since a derived object's several base subobjects are not necessarily all at the same address — this is one reason multiple-base layouts are more complex than single-base ones, but it is handled automatically by the language and never something correct code needs to compute manually.

HybridCar car;
Engine* enginePtr = &car;   // upcast to one base
Battery* batteryPtr = &car; // upcast to the other base
Vehicle* vehiclePtr = &car; // upcast to the shared virtual base, unambiguous

When Multiple Inheritance Is Genuinely Useful

Multiple inheritance earns its complexity mainly in one pattern: combining several independent, typically pure-abstract interfaces into one implementing class. Since interfaces (classes made only of pure virtual functions) usually carry no data and no diamond-prone shared state, this avoids most of multiple inheritance's sharp edges.

Report implements two independent interface contracts at once — a clean, low-risk use of multiple inheritance, since neither interface carries state that could collide or duplicate.

This is sometimes called mixin-style or interface-style multiple inheritance, and it is the pattern most C++ style guides consider safe to use freely.

class Printable
{
public:
    virtual void print() const = 0;
    virtual ~Printable() = default;
};

class Serializable
{
public:
    virtual std::string serialize() const = 0;
    virtual ~Serializable() = default;
};

class Report : public Printable, public Serializable
{
public:
    void print() const override { std::cout << "report\n"; }
    std::string serialize() const override { return "report-data"; }
};

Risks and Design Considerations of Multiple Inheritance

Multiple inheritance of classes that carry their own data and behavior (rather than pure interfaces) tends to create the problems this topic has covered: name conflicts requiring explicit qualification, diamond-shaped hierarchies requiring virtual inheritance, and initialization responsibilities that shift unexpectedly to the most-derived class. It also makes a class's overall responsibility harder to state in one sentence, since it is simultaneously 'is-a' several different things at once.

A hierarchy that needs virtual inheritance to work correctly is a signal worth pausing on: it usually means the design is combining state from multiple sources that would be clearer as explicitly-named member objects instead of implicitly-shared base subobjects.

Composition as an Alternative

Composition — giving a class member objects of the types it needs, rather than inheriting from them — sidesteps ambiguity, the diamond problem, and virtual inheritance entirely, at the cost of needing to forward specific operations explicitly instead of getting them automatically.

HybridCar has-an Engine and has-a GPS, rather than is-an Engine and is-a GPS. There is no ambiguity possible, because engine and gps are two clearly distinct named members, not overlapping base subobjects.

Composition is generally preferred over multiple inheritance of implementation whenever the relationship is really 'built from' rather than 'is fundamentally a kind of' — see the dedicated Encapsulation, Abstraction & OOP Design topic for a fuller treatment of composition versus inheritance.

class HybridCar
{
public:
    void start() { engine.start(); }
    void locate() { gps.locate(); }
private:
    Engine engine;
    GPS gps;
};

Interfaces as an Alternative to Implementation Multiple Inheritance

When a class genuinely needs to satisfy several distinct contracts (rather than reuse several implementations), inheriting from several pure-abstract interface classes — as shown earlier with Report — combines the benefits of multiple inheritance (satisfying multiple roles) without the risks of multiple inheritance of stateful implementation (no shared data, so no diamond, no ambiguous data members).

A practical rule of thumb: prefer at most one base class that carries real implementation and state, plus any number of pure-abstract interface bases. This keeps the 'is-a' relationships that carry data unambiguous while still allowing a class to declare that it fulfills multiple behavioral contracts.

Best Practices

1. Prefer inheriting from at most one base class with real data and state; combine additional roles through pure-abstract interface bases instead.

2. Use virtual inheritance whenever two or more bases you are combining already share a common ancestor that carries state — this avoids silently duplicated subobjects.

3. Resolve any name conflict explicitly with Base::member rather than restructuring code around the ambiguity error — explicit qualification states the intent directly.

4. When using virtual inheritance, have the most-derived class explicitly initialize the virtual base in its own member-initializer list — intermediate classes' initializers for that base will be bypassed, so relying on them there is misleading.

5. Reach for composition instead of multiple inheritance whenever the relationship is 'has-a' or 'is built from' rather than 'is fundamentally a kind of.'

6. Treat a hierarchy that requires virtual inheritance as a prompt to reconsider whether composition would express the same design more clearly.

Complete Example

HybridCar combines a shared, virtually-inherited state base (Vehicle, through Engine and Battery) with two stateless pure-abstract interface bases (Printable, Serializable) — the safer of the two multiple-inheritance patterns layered together deliberately.

Vehicle is constructed once, as a shared virtual base, using HybridCar's own Vehicle(1800) initializer

Engine and Battery are then constructed, each seeing the already-initialized shared Vehicle subobject print() and serialize() are unambiguous, single overrides satisfying the two interface contracts output reflects a single, shared weight of 1800 accessed through either path The program's output.

#include <iostream>
#include <string>

class Printable
{
public:
    virtual void print() const = 0;
    virtual ~Printable() = default;
};

class Serializable
{
public:
    virtual std::string serialize() const = 0;
    virtual ~Serializable() = default;
};

class Vehicle
{
public:
    explicit Vehicle(int w) : weight(w) {}
    int weight;
};

class Engine : public virtual Vehicle
{
public:
    Engine() : Vehicle(0) {}
};

class Battery : public virtual Vehicle
{
public:
    Battery() : Vehicle(0) {}
};

class HybridCar : public Engine, public Battery, public Printable, public Serializable
{
public:
    HybridCar() : Vehicle(1800), Engine(), Battery() {}

    void print() const override
    {
        std::cout << "HybridCar, weight " << weight << "kg\n";
    }

    std::string serialize() const override
    {
        return "HybridCar:weight=" + std::to_string(weight);
    }
};

int main()
{
    HybridCar car;
    car.print();
    std::cout << car.serialize() << '\n';
    return 0;
}
HybridCar, weight 1800kg
HybridCar:weight=1800

Multiple Inheritance and Diamond Mental Model

Picture ordinary (non-virtual) multiple inheritance as each base bringing its own private copy of anything it inherited, even from a common ancestor — nothing is automatically shared. Picture virtual inheritance as marking a specific ancestor 'there is only ever one of these, no matter how many paths lead to it.'

The structural difference virtual inheritance makes: it collapses what would otherwise be duplicate ancestor subobjects into one.

Non-virtual (duplicated):                 Virtual (shared):

   Vehicle(A)   Vehicle(B)                          Vehicle
       |            |                              /      \
    Engine       Battery                       Engine    Battery
         \        /                                 \      /
          HybridCar                                 HybridCar
     (two separate Vehicles)                (one shared Vehicle)

Quick Reference

A quick-reference table summarizing the core syntax and rules from this topic.

Concept                      | Syntax                                          | Meaning
Multiple bases                | class D : public B1, public B2 {};             | D inherits from both B1 and B2
Name conflict resolution      | obj.Base::member                                | Explicitly select which base's member to use
Virtual inheritance           | class B : public virtual Shared {};             | Shared base is deduplicated across multiple paths
Virtual base initialization   | (in most-derived class's init list) Shared(...) | Only the most-derived class's initializer runs
Construction order            | virtual bases, then direct bases in listed order, then derived | Determines the sequence of constructor calls
Safe MI pattern               | multiple pure-abstract interface bases          | No shared state, so no ambiguity or diamond risk
Composition alternative       | class D { B1 b1; B2 b2; };                      | Has-a instead of is-a; no ambiguity possible

Key Takeaways

Multiple inheritance lets a class list more than one direct base, combining their members; base subobjects are constructed in base-list order and destroyed in the exact reverse order.

A name that exists in more than one base becomes ambiguous when referred to unqualified through the derived class; resolve it with explicit Base::member qualification.

The diamond problem occurs when two bases share a common ancestor and a further class inherits from both, producing duplicate ancestor subobjects unless virtual inheritance is used.

Virtual inheritance (public virtual Base) makes a shared ancestor appear exactly once in the most-derived object, and shifts responsibility for initializing that shared subobject to the most-derived class.

Multiple inheritance of several stateless, pure-abstract interfaces is generally safe; multiple inheritance of several stateful implementation classes is where ambiguity and the diamond problem actually bite.

Composition (has-a, via member objects) avoids all of these issues and is usually the better default whenever code reuse, not a genuine multi-role is-a relationship, is the goal.

Two base classes, Reader and Writer, each declare a member function named access(). A class FileHandle inherits publicly from both. What happens if code calls handle.access(), and how would you fix it?

The call is ambiguous — the compiler cannot tell whether you mean Reader::access() or Writer::access(), since both are equally valid unqualified matches. It must be resolved either by qualifying the call explicitly (handle.Reader::access() or handle.Writer::access()), or by FileHandle defining its own access() member that internally calls whichever base version(s) it needs.

Explain, using a Base class inherited by both Left and Right, and a Bottom class inheriting from both Left and Right, why Bottom ends up with two Base subobjects if the inheritance is not virtual, and what changes if it is.

Without virtual inheritance, each of Left and Right independently contains its own complete Base subobject, so Bottom — inheriting from both — contains two separate, unrelated Base subobjects (one via Left, one via Right), and unqualified access to a Base member through Bottom is ambiguous. Declaring Left and Right's inheritance from Base as virtual (public virtual Base) makes Base a shared virtual base: Bottom then contains exactly one Base subobject, referenced by both Left and Right, and unqualified access to a Base member through Bottom is unambiguous.

In a virtual inheritance hierarchy where Base is a shared virtual base of Left and Right, and Bottom inherits from both, which class's member-initializer list actually initializes the shared Base subobject, and what happens to Left's and Right's own Base(...) initializers?

The most-derived class, Bottom, is directly responsible for initializing the shared virtual base — its own Base(...) entry in its member-initializer list is what actually constructs that single subobject. Left's and Right's own Base(...) initializers are bypassed when Left or Right is not the most-derived class in the object being constructed (i.e. whenever a class further derived, like Bottom, exists), even though those initializers remain syntactically present.

Give an example of a case where multiple inheritance is a reasonable design choice, and explain why it avoids the usual risks of multiple inheritance.

Inheriting from several pure-abstract interface classes (each made only of pure virtual functions, with no data members) is a reasonable use — for example, a Report class implementing both a Printable interface and a Serializable interface. Because interfaces like these carry no state, there is no shared ancestor state to duplicate and no data-member name conflicts are likely, so the diamond problem and most ambiguity risks simply do not arise.

A class HybridCar inherits from Engine and Battery, both of which independently inherit from Vehicle (non-virtually). Composition is proposed as an alternative design. Describe what that alternative would look like and why it sidesteps the ambiguity entirely.

Instead of HybridCar : public Engine, public Battery, the composition alternative gives HybridCar its own named Engine and Battery member objects (e.g. Engine engine; Battery battery;) and forwards whichever operations it needs to them explicitly (e.g. void start() { engine.start(); }). Because engine and battery are two distinct, separately-named member objects rather than overlapping base subobjects, there is no possibility of an ambiguous shared ancestor — any Vehicle-related state each one carries stays clearly separate unless the design explicitly decides to share it.

This code compiles and runs. Explain why d.x alone would not compile, and what d.B::x and d.C::x each refer to.

D inherits from both B and C without virtual inheritance, and both B and C independently inherit from A, so D contains two separate A subobjects: one reached through B, one reached through C. d.x alone would be ambiguous, since the compiler cannot tell which of the two A::x members you mean. d.B::x explicitly selects the x belonging to the A subobject reached via B, and d.C::x selects the one reached via C — they are two distinct int objects, both initialized to 1 by A's default member initializer, so the output is '1 1'.

Rewrite the hierarchy from the previous example (A with member x, B and C each inheriting from A, D inheriting from both B and C) so that D contains only one shared A subobject, using virtual inheritance. Show that d.x compiles without qualification and prints the shared value after being set once.

Declaring B's and C's inheritance from A as virtual makes A a shared virtual base, so D contains exactly one A subobject instead of two. d.x is then unambiguous, since there is only one x to refer to regardless of which path (B or C) it is reached through. Setting d.x = 42; and reading d.x afterward both refer to that single shared object, so the program prints 42.

Write two pure-abstract interface classes, Flyable (pure virtual void fly() const) and Swimmable (pure virtual void swim() const), each with a virtual destructor. Write a Duck class that inherits from both and implements both methods by printing 'flying' and 'swimming' respectively. In main, call both methods on a Duck object.

Duck inherits from two stateless pure-abstract interfaces and provides one override for each. Because neither Flyable nor Swimmable carries data or shares a common stateful ancestor, there is no ambiguity or diamond problem — this is the safe, interface-combining style of multiple inheritance. The program prints 'flying' then 'swimming'.

This is non-virtual multiple inheritance (Left and Right each plainly inherit from Base, not virtually). What does constructing a Bottom object print, and how many times does 'Base' appear?

'Base' appears twice, once for each independent Base subobject: one belonging to Left's chain, one belonging to Right's chain. Construction order follows the base-class list: Left is constructed first (which first constructs its own Base subobject, printing 'Base ', then runs Left's body, printing 'Left '), then Right is constructed the same way (printing 'Base ' again, then 'Right '), then Bottom's own body runs last (printing 'Bottom'). Full output: 'Base Left Base Right Bottom'.

class A { public: int x = 1; };
class B : public A {};
class C : public A {};
class D : public B, public C {};
int main()
{
    D d;
    std::cout << d.B::x << ' ' << d.C::x;
    return 0;
}
class Base { public: Base() { std::cout << "Base "; } };
class Left : public Base { public: Left() { std::cout << "Left "; } };
class Right : public Base { public: Right() { std::cout << "Right "; } };
class Bottom : public Left, public Right { public: Bottom() { std::cout << "Bottom"; } };
int main()
{
    Bottom b;
    return 0;
}