Skip to content

Multiple Inheritance — Diamond Problem, Virtual Inheritance, Interfaces

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about Multiple Inheritance. We cover key concepts, practical examples, and best practices to help you master this topic.

C++ multiple inheritance allows a class to inherit from two or more base classes, with virtual inheritance resolving the diamond ambiguity that arises when two bases share a common ancestor.

What You'll Learn

You will derive a class from multiple base classes, recognize the diamond problem and understand why it causes ambiguity, use virtual inheritance to ensure a single shared subobject, design interface classes for mixin-style multiple inheritance, and understand construction order and casting in multiple inheritance hierarchies.

Why It Matters

Multiple inheritance is powerful but controversial. When used well, it enables mixin composition and interface implementation without the boilerplate of other languages. When misused, it creates confusing hierarchies and hard-to-maintain code. Mastering multiple inheritance gives you a tool for elegant design in frameworks, GUI libraries, and protocol implementations.

Learning Path

graph LR
    A["17: Abstract Classes"] --> B["18: Multiple Inheritance"]
    B --> C["19: Operator Overloading"]
    C --> D["20: Copy & Move Semantics"]
    style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style B fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style C fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style D fill:#4a90d9,stroke:#2c5f8a,color:#fff

Basic Multiple Inheritance

#include <iostream>

struct A {
    void foo() { std::cout << "A::foo\n"; }
};

struct B {
    void bar() { std::cout << "B::bar\n"; }
};

struct C : public A, public B {
    void baz() { std::cout << "C::baz\n"; }
};

int main() {
    C c;
    c.foo();  // from A
    c.bar();  // from B
    c.baz();  // from C
}

The Diamond Problem

#include <iostream>

struct Base {
    int value_ = 0;
};

struct Left : public Base {
    void setLeft(int v) { value_ = v; }
};

struct Right : public Base {
    void setRight(int v) { value_ = v; }
};

struct Derived : public Left, public Right {
    void show() {
        // std::cout << value_;  // Error: ambiguous
        std::cout << Left::value_ << "\n";
        std::cout << Right::value_ << "\n";
    }
};

int main() {
    Derived d;
    d.setLeft(10);
    d.setRight(20);
    
    // d.value_ = 5;  // Error: ambiguous
    d.Left::value_ = 5;  // OK: explicit qualification
    
    // Base* bp = &d;  // Error: ambiguous which Base
    Base* bl = static_cast<Left*>(&d);
    Base* br = static_cast<Right*>(&d);
    
    std::cout << bl->value_ << "\n";  // 5 (Left's Base)
    std::cout << br->value_ << "\n";  // 20 (Right's Base)
}

Without virtual inheritance, Derived contains two separate Base subobjects. This is the diamond problem.

Virtual Inheritance

Virtual inheritance ensures that the base class exists only once in the hierarchy, regardless of how many paths lead to it.

#include <iostream>

struct Base {
    int value_ = 0;
};

struct Left : virtual public Base {
    void setLeft(int v) { value_ = v; }
};

struct Right : virtual public Base {
    void setRight(int v) { value_ = v; }
};

struct Derived : public Left, public Right {
    void show() {
        std::cout << value_ << "\n";  // No ambiguity: single Base
    }
};

int main() {
    Derived d;
    d.setLeft(10);
    d.setRight(20);
    
    std::cout << d.value_ << "\n";  // 20 (last write wins, single subobject)
    
    Base* bp = &d;  // No ambiguity
    bp->value_ = 5;
    
    d.show();  // 5
}

With virtual inheritance, setLeft(10) and setRight(20) operate on the same value_. The last write wins, and there is no ambiguity.

Construction with Virtual Bases

The most derived class must construct all virtual bases, even if intermediate bases also initialize them.

#include <iostream>

class Animal {
public:
    Animal(const std::string& name) {
        std::cout << "Animal: " << name << "\n";
    }
};

class Mammal : virtual public Animal {
public:
    Mammal() : Animal("Mammal default") {
        std::cout << "Mammal\n";
    }
};

class Bird : virtual public Animal {
public:
    Bird() : Animal("Bird default") {
        std::cout << "Bird\n";
    }
};

class Bat : public Mammal, public Bird {
public:
    Bat() : Animal("Bat"), Mammal(), Bird() {
        std::cout << "Bat\n";
    }
};

int main() {
    Bat b;
}

Expected output:

Animal: Bat
Mammal
Bird
Bat

The most derived class (Bat) initializes the virtual base directly. The initializers in Mammal and Bird are ignored for the virtual base.

Pointer Adjustment in Multiple Inheritance

#include <iostream>

struct A { virtual ~A() = default; int a; };
struct B { virtual ~B() = default; int b; };
struct C : A, B { int c; };

int main() {
    C c;
    A* pa = &c;
    B* pb = &c;
    
    std::cout << "&c: " << &c << "\n";
    std::cout << "pa: " << pa << "\n";
    std::cout << "pb: " << pb << "\n";  // different address
    
    // Casting between bases adjusts the pointer
    B* fromA = dynamic_cast<B*>(pa);
    std::cout << "fromA: " << fromA << "\n";  // same as pb
}

In multiple inheritance, a pointer to one base may differ from a pointer to another base. The compiler generates pointer adjustment code when casting between bases.

Mixin Style with Multiple Inheritance

Mixins are small classes that add functionality. Multiple inheritance composes them.

#include <iostream>
#include <string>

class Printable {
public:
    virtual std::string str() const = 0;
    void print() const { std::cout << str() << "\n"; }
};

class Named {
private:
    std::string name_;
public:
    Named(const std::string& name) : name_(name) {}
    const std::string& name() const { return name_; }
};

class Countable {
private:
    static int counter_;
    int id_;
public:
    Countable() : id_(++counter_) {}
    int id() const { return id_; }
};
int Countable::counter_ = 0;

class Widget : public Printable, public Named, public Countable {
public:
    Widget(const std::string& name) : Named(name) {}
    
    std::string str() const override {
        return "Widget(" + name() + ", id=" + std::to_string(id()) + ")";
    }
};

int main() {
    Widget w1("Button");
    Widget w2("Slider");
    w1.print();
    w2.print();
}

Expected output:

Widget(Button, id=1)
Widget(Slider, id=2)

Common Mistakes

Mistake 1: Using Multiple Inheritance for Implementation Reuse

Multiple inheritance for code reuse often leads to confusing hierarchies. Prefer Composition Over Inheritance for reuse. Use multiple inheritance primarily for interface separation.

Mistake 2: Forgetting Virtual Inheritance

If you know a class will serve as a base in a diamond hierarchy, declare its inheritance virtual from the start. Changing to virtual later can break layout.

Mistake 3: Ambiguous Function Calls

struct A { void f() {} };
struct B { void f() {} };
struct C : A, B {};
C c;
// c.f();  // Error: ambiguous
c.A::f();  // OK: explicit

Mistake 4: Casting Between Incompatible Bases

Use dynamic_cast or static_cast explicitly when casting between bases in a multiple inheritance hierarchy. C-style casts may perform incorrect pointer adjustments.

Mistake 5: Virtual Base Size Overhead

Virtual inheritance adds runtime overhead (additional pointers for offset calculation). Use it only when the diamond problem exists, not as a default.

Practice Questions

  1. What is the diamond problem? How does virtual inheritance solve it?
  2. Who is responsible for constructing virtual base classes?
  3. Why might static_cast<Base*>(derivedPtr) produce a different address in multiple inheritance?
  4. Write a Logger mixin class that adds logging to any class via multiple inheritance.
  5. When is multiple inheritance preferable to composition?

Challenge

Design a class hierarchy for a game: GameObject (position, rotation), Renderable (virtual draw), PhysicsBody (virtual update), and Player that inherits from all three (with virtual inheritance from GameObject). Demonstrate a game loop processing a vector of GameObject*.

FAQ

Does Java support multiple inheritance?

Java does not support multiple inheritance of classes, but a class can implement multiple interfaces. C++ allows both.

What is the size overhead of virtual inheritance?

Each virtual base adds a pointer (or offset) to each derived class object, typically 4 or 8 bytes per virtual base per object.

Can I use dynamic_cast with multiple inheritance?

Yes. dynamic_cast can cast across the hierarchy, including between unrelated bases in the same derived object, performing the necessary pointer adjustments.

Should I use virtual inheritance by default?

No. Virtual inheritance has runtime and memory overhead. Only use it when you actually have a diamond hierarchy.

What are 'mixin' classes?

Mixins are small classes that provide specific functionality (e.g., Printable, Serializable, Comparable). They are composed using multiple inheritance.

How does construction order work with virtual bases?

Virtual bases are constructed first (in depth-first, left-to-right order), then non-virtual bases, then members, then the derived class body.

Mini Project

Build a Serialization system using multiple inheritance:

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

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

class JSONSerializable {
public:
    virtual ~JSONSerializable() = default;
    virtual std::string toJSON() const = 0;
    virtual void fromJSON(const std::string&) = 0;
};

class Person : public Serializable, public JSONSerializable {
private:
    std::string name_;
    int age_;
    
public:
    Person() = default;
    Person(const std::string& name, int age) : name_(name), age_(age) {}
    
    std::string serialize() const override {
        return name_ + "|" + std::to_string(age_);
    }
    
    void deserialize(const std::string& data) override {
        auto delim = data.find('|');
        name_ = data.substr(0, delim);
        age_ = std::stoi(data.substr(delim + 1));
    }
    
    std::string toJSON() const override {
        return "{\"name\":\"" + name_ + "\",\"age\":" + std::to_string(age_) + "}";
    }
    
    void fromJSON(const std::string& json) override {
        // Simplified JSON parsing
    }
};

int main() {
    Person p1("Alice", 30);
    std::cout << p1.serialize() << "\n";
    std::cout << p1.toJSON() << "\n";
    
    Person p2;
    p2.deserialize("Bob|25");
    std::cout << p2.toJSON() << "\n";
}

What's Next

Multiple inheritance composes interfaces and implementations. The next lesson covers operator overloading: how to make custom types work with C++ operators like +, -, <<, and >>.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro