What Is Multiple Inheritance?

Every hierarchy in this chapter so far has been single inheritance: each derived class named exactly one parent. C++ does not stop at one. Multiple inheritance lets a derived class inherit members from more than one parent class at once. You write it by listing the bases after the colon, separated by commas, each with its own access specifier:

class Compound : public FirstBase, public SecondBase

A pipe organ stop is a good candidate. Physically it is two separate pieces of engineering bolted into one named thing: a rank of pipes standing on a windchest, and a drawknob at the console that admits wind to that rank. Neither one is a kind of the other, and neither one owns the other, so a stop can reasonably be built as both.

#include <iostream>
#include <string>
#include <string_view>

class PipeRank
{
public:
    PipeRank(int pipeCount, int pressureMm)
        : m_pipeCount{ pipeCount }, m_pressureMm{ pressureMm }
    {
    }

    int pipeCount() const { return m_pipeCount; }
    int pressureMm() const { return m_pressureMm; }

private:
    int m_pipeCount{};
    int m_pressureMm{};
};

class Drawknob
{
public:
    explicit Drawknob(std::string_view engraving)
        : m_engraving{ engraving }
    {
    }

    const std::string& engraving() const { return m_engraving; }
    bool isDrawn() const { return m_drawn; }
    void draw() { m_drawn = true; }

private:
    std::string m_engraving{};
    bool m_drawn{};
};

// OrganStop inherits from both bases, listed after a colon and separated by a comma
class OrganStop : public PipeRank, public Drawknob
{
public:
    OrganStop(std::string_view engraving, int pipeCount, int pressureMm, int footPitch)
        : PipeRank{ pipeCount, pressureMm }, Drawknob{ engraving }, m_footPitch{ footPitch }
    {
    }

    int footPitch() const { return m_footPitch; }

private:
    int m_footPitch{};
};

int main()
{
    OrganStop trompette{ "Trompette", 61, 95, 8 };
    trompette.draw();

    std::cout << trompette.engraving() << ' ' << trompette.footPitch() << " ft" << '\n';
    std::cout << "pipes: " << trompette.pipeCount() << '\n';
    std::cout << "wind: " << trompette.pressureMm() << " mm" << '\n';
    std::cout << "drawn: " << std::boolalpha << trompette.isDrawn() << '\n';

    return 0;
}

Output:

Trompette 8 ft
pipes: 61
wind: 95 mm
drawn: true

The rules you already know all carry over, they just apply once per base. Each base needs its own entry in the member initializer list. Each base contributes its public interface to the derived class. Each base's access specifier is chosen independently, so class OrganStop : public PipeRank, private Drawknob would be a perfectly legal, if odd, mixture.

What the Compiler Actually Builds

A derived object contains one base class subobject for every base you list, laid out one after another, followed by the derived class's own members. That single fact explains almost everything else in this lesson, including the errors further down.

Because there are two subobjects, there are two base constructors to run, and the order is fixed by the base list, not by anything you write:

#include <iostream>

class Windchest
{
public:
    Windchest() { std::cout << "Windchest built" << '\n'; }
    ~Windchest() { std::cout << "Windchest scrapped" << '\n'; }
};

class Drawknob
{
public:
    Drawknob() { std::cout << "Drawknob built" << '\n'; }
    ~Drawknob() { std::cout << "Drawknob scrapped" << '\n'; }
};

class StoppedFlute : public Windchest, public Drawknob
{
public:
    StoppedFlute() { std::cout << "StoppedFlute built" << '\n'; }
    ~StoppedFlute() { std::cout << "StoppedFlute scrapped" << '\n'; }
};

int main()
{
    StoppedFlute gedackt{};

    return 0;
}

Output:

Windchest built
Drawknob built
StoppedFlute built
StoppedFlute scrapped
Drawknob scrapped
Windchest scrapped

Bases are constructed left to right across the base list, then the derived class body runs. Destruction reverses that exactly. Swapping public Windchest, public Drawknob around in the class header swaps the construction order too, which is why the base list is a real design decision and not just punctuation.

Warning
Writing the bases in a different order in the member initializer list does not change the construction order. It only makes the code lie about what happens. GCC catches this with -Wreorder, which is enabled by -Wall, so keep the initializer list in the same order as the base list.

Mixins: One Capability per Base

A mixin is a small class written to be inherited from in order to add one property or behaviour to another class. The name says the intent: it is meant to be mixed into something, not instantiated on its own. A mixin usually has no constructor worth calling, no identity, and no meaning as a standalone object.

Multiple inheritance is what makes mixins practical, because a class can take on several of them at once. Here three mixins each add one adjustable property of an enclosed organ stop:

#include <iostream>

class Tunable
{
public:
    void tuneTo(int cents) { m_centsOffset = cents; }
    int centsOffset() const { return m_centsOffset; }

private:
    int m_centsOffset{};
};

class Enclosed
{
public:
    void setShutters(int percent) { m_shutterPercent = percent; }
    int shutterPercent() const { return m_shutterPercent; }

private:
    int m_shutterPercent{};
};

class Tremulated
{
public:
    void setTremulant(bool running) { m_tremulantRunning = running; }
    bool tremulantRunning() const { return m_tremulantRunning; }

private:
    bool m_tremulantRunning{};
};

class SwellFlute : public Tunable, public Enclosed, public Tremulated
{
};

int main()
{
    SwellFlute rohrflute{};
    rohrflute.Tunable::tuneTo(-3);
    rohrflute.Enclosed::setShutters(40);
    rohrflute.Tremulated::setTremulant(true);

    std::cout << "offset: " << rohrflute.centsOffset() << " cents" << '\n';
    std::cout << "shutters: " << rohrflute.shutterPercent() << " percent" << '\n';
    std::cout << "tremulant: " << std::boolalpha << rohrflute.tremulantRunning() << '\n';

    return 0;
}

Output:

offset: -3 cents
shutters: 40 percent
tremulant: true

SwellFlute has an empty body and still has three settable properties. None of the mutator calls needed the Tunable:: and Enclosed:: prefixes, since no two mixins declare the same name, but writing them anyway records which mixin each call belongs to and keeps the call site working if a fourth mixin later introduces a clash.

Key Concept
A mixin is a base class that exists purely to be inherited from. Judge one by whether it adds a single, self-contained capability, not by whether it describes what the derived object is.

Advanced: A Mixin That Knows Its Derived Class

Mixins add functionality rather than define an interface, so they rarely use virtual functions (covered in the next chapter). When a mixin needs to be customised per derived class, the usual tool is a template instead. That leads to a striking-looking trick: the derived class passes itself to its own base as a template argument. This is the Curiously Recurring Template Pattern, or CRTP.

#include <iostream>
#include <string>
#include <string_view>

template <typename T>
class Nameplate
{
public:
    void printFace() const
    {
        const T& stop{ static_cast<const T&>(*this) };
        std::cout << stop.engraving() << " / " << stop.footPitch() << " ft" << '\n';
    }
};

// StopFace hands itself to its own base as a template argument
class StopFace : public Nameplate<StopFace>
{
public:
    StopFace(std::string_view engraving, int footPitch)
        : m_engraving{ engraving }, m_footPitch{ footPitch }
    {
    }

    const std::string& engraving() const { return m_engraving; }
    int footPitch() const { return m_footPitch; }

private:
    std::string m_engraving{};
    int m_footPitch{};
};

int main()
{
    StopFace bourdon{ "Bourdon", 16 };
    bourdon.printFace();

    return 0;
}

Output:

Bourdon / 16 ft

Nameplate<StopFace> knows the exact type of its own derived class, so it can cast this down and call members that Nameplate itself never declared. That is the whole pattern in one line: class Compound : public Capability<Compound>.

Two Bases, One Name

Now for the cost. Name lookup on a derived object searches the derived class first, then its bases. With one base that search has one place to go. With two, it can find the same name twice, and C++ does not guess.

At a large console, a stop can often be operated two ways: a drawknob by the player's hand, and a toe piston by the player's foot. Both are engraved, and the engravings are not always the same text.

The following program is deliberately broken.

#include <iostream>
#include <string>
#include <string_view>

class Drawknob
{
public:
    explicit Drawknob(std::string_view engraving)
        : m_engraving{ engraving }
    {
    }

    const std::string& engraving() const { return m_engraving; }

private:
    std::string m_engraving{};
};

class ToePiston
{
public:
    explicit ToePiston(std::string_view engraving)
        : m_engraving{ engraving }
    {
    }

    const std::string& engraving() const { return m_engraving; }

private:
    std::string m_engraving{};
};

class ReversibleStop : public Drawknob, public ToePiston
{
public:
    ReversibleStop(std::string_view knobFace, std::string_view pistonFace)
        : Drawknob{ knobFace }, ToePiston{ pistonFace }
    {
    }
};

int main()
{
    ReversibleStop tuba{ "Tuba Mirabilis", "Tuba" };
    std::cout << tuba.engraving() << '\n'; // whose engraving?

    return 0;
}

The compiler refuses to pick a side:

s.cpp: In function 'int main()':
s.cpp:45:23: error: request for member 'engraving' is ambiguous
   45 |     std::cout << tuba.engraving() << '\n'; // whose engraving?
      |                       ^~~~~~~~~
  • there are 2 candidates
    • candidate 1: 'const std::string& ToePiston::engraving() const'
      s.cpp:27:24:
         27 |     const std::string& engraving() const { return m_engraving; }
            |                        ^~~~~~~~~
    • candidate 2: 'const std::string& Drawknob::engraving() const'
      s.cpp:13:24:
         13 |     const std::string& engraving() const { return m_engraving; }
            |                        ^~~~~~~~~

This is a hard compile error, not a warning and not a silent pick of the first base. Nothing about the two functions matters here except their names: identical signatures, different signatures, one const and one not, it is ambiguous either way, because the ambiguity is settled during name lookup before overload resolution ever gets a turn.

There are two ways out, and the fixed program below uses both. Qualifying the call with Base:: says which subobject you mean at that one call site. A using declaration inside the derived class picks one of the inherited names as the default for every unqualified call.

#include <iostream>
#include <string>
#include <string_view>

class Drawknob
{
public:
    explicit Drawknob(std::string_view engraving)
        : m_engraving{ engraving }
    {
    }

    const std::string& engraving() const { return m_engraving; }

private:
    std::string m_engraving{};
};

class ToePiston
{
public:
    explicit ToePiston(std::string_view engraving)
        : m_engraving{ engraving }
    {
    }

    const std::string& engraving() const { return m_engraving; }

private:
    std::string m_engraving{};
};

class ReversibleStop : public Drawknob, public ToePiston
{
public:
    ReversibleStop(std::string_view knobFace, std::string_view pistonFace)
        : Drawknob{ knobFace }, ToePiston{ pistonFace }
    {
    }

    using Drawknob::engraving; // unqualified calls now resolve to Drawknob::engraving
};

int main()
{
    ReversibleStop tuba{ "Tuba Mirabilis", "Tuba" };

    std::cout << "knob: " << tuba.Drawknob::engraving() << '\n';
    std::cout << "piston: " << tuba.ToePiston::engraving() << '\n';
    std::cout << "unqualified: " << tuba.engraving() << '\n';

    return 0;
}

Output:

knob: Tuba Mirabilis
piston: Tuba
unqualified: Tuba Mirabilis

Both fixes are cheap in isolation. They stop being cheap when a class pulls in four or six bases that pull in bases of their own, because every colliding name has to be found and disambiguated by hand, and a name that is unambiguous today becomes an error the day someone adds a base that happens to use it.

The Diamond Problem

The second problem is worse, because the duplication is in the object itself rather than in a name. It appears when a class inherits from two classes that each inherit from the same base, producing a diamond shaped graph:

      PipeRank
      /      \
 ReedRank   FlueRank
      \      /
     CornetStop

Reed ranks and flue ranks are the two families of organ pipe, so both are kinds of PipeRank. A cornet is a compound stop that speaks several ranks at once, so it is built from both. The following program is deliberately broken.

#include <iostream>

class PipeRank
{
public:
    explicit PipeRank(int pipeCount)
        : m_pipeCount{ pipeCount }
    {
    }

    int pipeCount() const { return m_pipeCount; }

private:
    int m_pipeCount{};
};

class ReedRank : public PipeRank
{
public:
    explicit ReedRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class FlueRank : public PipeRank
{
public:
    explicit FlueRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class CornetStop : public ReedRank, public FlueRank
{
public:
    CornetStop(int reedPipes, int fluePipes)
        : ReedRank{ reedPipes }, FlueRank{ fluePipes }
    {
    }
};

int main()
{
    CornetStop cornet{ 61, 183 };
    std::cout << cornet.pipeCount() << '\n'; // which PipeRank subobject?

    return 0;
}
s.cpp: In function 'int main()':
s.cpp:47:25: error: request for member 'pipeCount' is ambiguous
   47 |     std::cout << cornet.pipeCount() << '\n'; // which PipeRank subobject?
      |                         ^~~~~~~~~
  • there are 2 candidates
    • candidate 1: 'int PipeRank::pipeCount() const'
      s.cpp:11:9:
         11 |     int pipeCount() const { return m_pipeCount; }
            |         ^~~~~~~~~
    • candidate 2: 'int PipeRank::pipeCount() const'

Read those last two lines again. The two candidates are the same function, declared once, on line 11. CornetStop did not inherit two functions named pipeCount, it inherited two whole PipeRank subobjects, and the one function can be reached through either of them.

Give PipeRank a constructor that announces itself and the duplication becomes impossible to miss:

#include <iostream>

class PipeRank
{
public:
    explicit PipeRank(int pipeCount)
        : m_pipeCount{ pipeCount }
    {
        std::cout << "PipeRank subobject holding " << m_pipeCount << " pipes" << '\n';
    }

    int pipeCount() const { return m_pipeCount; }

private:
    int m_pipeCount{};
};

class ReedRank : public PipeRank
{
public:
    explicit ReedRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class FlueRank : public PipeRank
{
public:
    explicit FlueRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class CornetStop : public ReedRank, public FlueRank
{
public:
    CornetStop(int reedPipes, int fluePipes)
        : ReedRank{ reedPipes }, FlueRank{ fluePipes }
    {
    }
};

int main()
{
    CornetStop cornet{ 61, 183 };

    std::cout << "reed side: " << cornet.ReedRank::pipeCount() << '\n';
    std::cout << "flue side: " << cornet.FlueRank::pipeCount() << '\n';

    return 0;
}

Output:

PipeRank subobject holding 61 pipes
PipeRank subobject holding 183 pipes
reed side: 61
flue side: 183

One PipeRank constructor call per path through the diamond, and two independent pipe counts inside one object. Qualifying with ReedRank:: and FlueRank:: compiles, but notice what it has really done: it has made the caller responsible for knowing that the object carries two copies of its own base.

Danger
Duplicated base state is a correctness bug waiting to happen, not just an inconvenience. Code that writes through one path and reads through the other sees stale values, and the two copies drift apart silently because nothing in the type system says they were ever meant to agree.

Virtual Base Classes: One Shared Copy

When two copies are wrong, the language offers a way to say so. Declaring the shared base virtual in each intermediate class tells the compiler to give the most derived object a single, shared PipeRank subobject:

#include <iostream>

class PipeRank
{
public:
    explicit PipeRank(int pipeCount)
        : m_pipeCount{ pipeCount }
    {
        std::cout << "PipeRank subobject holding " << m_pipeCount << " pipes" << '\n';
    }

    int pipeCount() const { return m_pipeCount; }

private:
    int m_pipeCount{};
};

class ReedRank : virtual public PipeRank
{
public:
    explicit ReedRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class FlueRank : virtual public PipeRank
{
public:
    explicit FlueRank(int pipeCount)
        : PipeRank{ pipeCount }
    {
    }
};

class CornetStop : public ReedRank, public FlueRank
{
public:
    explicit CornetStop(int pipeCount)
        : PipeRank{ pipeCount }, ReedRank{ pipeCount }, FlueRank{ pipeCount }
    {
    }
};

int main()
{
    CornetStop cornet{ 244 };
    std::cout << "pipes: " << cornet.pipeCount() << '\n';

    return 0;
}

Output:

PipeRank subobject holding 244 pipes
pipes: 244

One constructor call, one copy, and cornet.pipeCount() needs no qualification because there is now only one subobject it could mean. Two details are worth flagging before you meet them properly: CornetStop has to initialize PipeRank itself, and the PipeRank{ pipeCount } calls written inside ReedRank and FlueRank are ignored when those classes are used as part of a larger object. Virtual bases have a whole lesson of their own later in the course, in the chapter on virtual functions.

Deciding Whether You Need It

Almost everything multiple inheritance does can be done another way, usually with less ceremony:

Situation Reach for
The new type genuinely is a kind of one existing type Single inheritance
The new type is made out of other objects Composition: hold them as members
Several unrelated classes need the same small self-contained capability A mixin, inherited alongside whatever else
Several unrelated classes must be usable through one shared interface An interface class, covered in the next chapter
Two independent, non-overlapping abstractions genuinely describe one object Multiple inheritance

The trade is real, so it is worth knowing how other languages judged it. Some object oriented languages, such as Smalltalk and PHP, do not offer multiple inheritance at all. Java and C# allow a class only one base class but let it implement any number of interfaces, on the grounds that inheriting behaviour and data from several places is what causes the trouble, while inheriting a pure interface does not.

C++ kept the full feature, and the standard library uses it. The iostream hierarchy that std::cin and std::cout belong to is built with multiple inheritance: std::iostream derives from both std::istream and std::ostream, and those two derive virtually from a common base so a bidirectional stream ends up with one copy of the shared stream state rather than two. It is the diamond of this lesson, solved with virtual bases, in code you have used since your first program.

Best Practice
Avoid multiple inheritance unless the alternatives lead to more complexity. When you do use it, prefer bases that are small, independent, and free of shared ancestors.

Summary

Multiple inheritance lets a derived class inherit from more than one parent by listing the bases after the colon, separated by commas: class Compound : public FirstBase, public SecondBase. The derived object contains one base class subobject per base, constructed left to right across the base list and destroyed in reverse.

A mixin is a small class written to be inherited from in order to add one capability, rather than to be instantiated on its own. Because mixins customise through templates rather than virtual functions, they are often templated, and a derived class can even pass itself as the template argument to its own base: class Compound : public Capability<Compound>, the Curiously Recurring Template Pattern.

Two hazards come with the feature:

Hazard What you see How you deal with it
Two bases declare the same name error: request for member 'x' is ambiguous Qualify the call as object.Base::x(), or add a using Base::x; declaration in the derived class
Two bases share a base class, the diamond problem The same ambiguity error, plus two copies of the shared base's data Qualify to pick a copy, or declare the shared base virtual in both intermediate classes so only one copy exists

Most designs that reach for multiple inheritance can be rewritten with single inheritance, composition, or an interface class, which is why languages such as Java and C# permit only one base class while allowing many interfaces, and why Smalltalk and PHP leave the feature out entirely. C++ keeps it, std::cin and std::cout come from a hierarchy that relies on it, and it remains the right answer often enough to be worth understanding. Use it when two independent abstractions really do describe one object, and reach for something simpler the rest of the time.