CLOS Multiple Inheritance / method-combination
C++ supports multiple inheritance, allowing a class to simultaneously inherit from more than one base class. While convenient, when the same base class is inherited through multiple paths, the "diamond inheritance problem" occurs. This is resolved using virtual inheritance.
Syntax
// Multiple inheritance (can cause the diamond problem)
class Base {
public:
int value;
void show() { /* ... */ }
};
class DerivedA : public Base {};
class DerivedB : public Base {};
// Both DerivedA and DerivedB are inherited, resulting in two Base instances
class Diamond : public DerivedA, public DerivedB {};
// Diamond d;
// d.value; // Compile error: ambiguous which Base::value
// Virtual inheritance to resolve the diamond problem
class VBase {
public:
int value;
void show() { /* ... */ }
};
class VA : public virtual VBase {};
class VB : public virtual VBase {};
class VDiamond : public VA, public VB {}; // Only one VBase instance
VDiamond vd;
vd.value = 42; // OK: no ambiguity
vd.show();
Syntax Reference
| Syntax / Concept | Description |
|---|---|
| class D : public A, public B | Multiple inheritance declaration. D inherits all members of both A and B. |
| Diamond inheritance problem | When a common base class is inherited through multiple paths, its instance is duplicated, causing ambiguity. |
| class D : public virtual Base | Virtual inheritance declaration. Even when the same base class is inherited through multiple paths, only one shared instance exists. |
| Virtual base class constructor call | With virtual inheritance, the most derived class is responsible for directly calling the virtual base class constructor. |
| Scope resolution to resolve ambiguity | Without virtual inheritance, you can resolve ambiguity by explicitly specifying the base class: d.DerivedA::value. |
Sample Code
steinsgate_virt.cpp
#include <iostream>
#include <string>
// Virtual base class: LabMember (shared lab member information)
class LabMember {
protected:
std::string labMemberNo;
std::string realName;
public:
LabMember(const std::string& no, const std::string& name)
: labMemberNo(no), realName(name) {}
void showLabInfo() const {
std::cout << "Lab Member " << labMemberNo << ": " << realName << std::endl;
}
virtual ~LabMember() {}
};
// Intermediate class: Hacker (virtually inherits LabMember)
class Hacker : public virtual LabMember {
public:
Hacker(const std::string& no, const std::string& name)
: LabMember(no, name) {}
void hack(const std::string& target) const {
std::cout << realName << " hacks " << target << "." << std::endl;
}
};
// Intermediate class: Inventor (virtually inherits LabMember)
class Inventor : public virtual LabMember {
public:
Inventor(const std::string& no, const std::string& name)
: LabMember(no, name) {}
void invent(const std::string& gadget) const {
std::cout << realName << " invented \"" << gadget << "\"." << std::endl;
}
};
// Most derived class: OkabeRintaro
// Inherits both Hacker and Inventor
// The virtual base class constructor is called here
class OkabeRintaro : public Hacker, public Inventor {
public:
OkabeRintaro()
: LabMember("001", "Okabe Rintaro"), // Most derived class calls this
Hacker("001", "Okabe Rintaro"),
Inventor("001", "Okabe Rintaro") {}
void declareTitle() const {
std::cout << "I am Hououin Kyouma! Mad scientist and founder of the Future Gadget Lab!" << std::endl;
}
};
// Additional classes: MakiseKurisu
class Scientist : public virtual LabMember {
public:
Scientist(const std::string& no, const std::string& name)
: LabMember(no, name) {}
void research(const std::string& topic) const {
std::cout << realName << " presented research on \"" << topic << "\"." << std::endl;
}
};
class Debugger : public virtual LabMember {
public:
Debugger(const std::string& no, const std::string& name)
: LabMember(no, name) {}
void debug(const std::string& system) const {
std::cout << realName << " found and fixed a bug in \"" << system << "\"." << std::endl;
}
};
// MakiseKurisu: multiple inheritance of Scientist and Debugger
class MakiseKurisu : public Scientist, public Debugger {
public:
MakiseKurisu()
: LabMember("004", "Makise Kurisu"),
Scientist("004", "Makise Kurisu"),
Debugger("004", "Makise Kurisu") {}
void quip() const {
std::cout << realName << ": \"I'm not your assistant!\"" << std::endl;
}
};
int main() {
std::cout << "=== Multiple Inheritance and Virtual Inheritance Demo ===" << std::endl << std::endl;
OkabeRintaro okabe;
std::cout << "--- Okabe Rintaro ---" << std::endl;
okabe.showLabInfo();
okabe.declareTitle();
okabe.hack("SERN database");
okabe.invent("Future Gadget #8 (Time Leap Machine)");
std::cout << std::endl;
MakiseKurisu kurisu;
std::cout << "--- Makise Kurisu ---" << std::endl;
kurisu.showLabInfo();
kurisu.quip();
kurisu.research("Memory retention mechanism in time travel");
kurisu.debug("D-mail send/receive system");
std::cout << std::endl;
std::cout << "--- Via LabMember pointer ---" << std::endl;
LabMember* member1 = &okabe;
LabMember* member2 = &kurisu;
member1->showLabInfo();
member2->showLabInfo();
return 0;
}
g++ -std=c++11 steinsgate_virt.cpp -o steinsgate_virt ./steinsgate_virt === Multiple Inheritance and Virtual Inheritance Demo === --- Okabe Rintaro --- Lab Member 001: Okabe Rintaro I am Hououin Kyouma! Mad scientist and founder of the Future Gadget Lab! Okabe Rintaro hacks SERN database. Okabe Rintaro invented "Future Gadget #8 (Time Leap Machine)". --- Makise Kurisu --- Lab Member 004: Makise Kurisu Makise Kurisu: "I'm not your assistant!" Makise Kurisu presented research on "Memory retention mechanism in time travel". Makise Kurisu found and fixed a bug in "D-mail send/receive system". --- Via LabMember pointer --- Lab Member 001: Okabe Rintaro Lab Member 004: Makise Kurisu
Common mistake 1: Forgetting virtual and causing the diamond problem
Without virtual, inheriting a common base class through multiple paths duplicates the base class instance, making member access ambiguous.
// NG: Without virtual, accessing no is ambiguous
class LabMember { public: int no; };
class Hacker : public LabMember {};
class Inventor : public LabMember {};
class OkabeRintaro : public Hacker, public Inventor {};
OkabeRintaro obj;
// obj.no = 1; // Compile error: ambiguous
OK: Add virtual to intermediate classes.
// OK: Virtual inheritance merges the instances into one
class HackerV : public virtual LabMember {};
class InventorV : public virtual LabMember {};
class OkabeV : public HackerV, public InventorV {};
OkabeV okabe;
okabe.no = 1; // OK: no ambiguity
Common mistake 2: Not calling the virtual base class constructor in the most derived class
With virtual inheritance, the most derived class must directly call the virtual base class constructor. If the virtual base class has no default constructor, the most derived class must explicitly call it.
// NG: If VBase has no default constructor, VDiamond must call it explicitly
class VBase { public: VBase(int v) {} };
class VA : public virtual VBase { public: VA() : VBase(0) {} };
class VB : public virtual VBase { public: VB() : VBase(0) {} };
// class VDiamond : public VA, public VB {}; // Compile error: no default constructor for VBase
OK: Most derived class calls VBase directly.
// OK: Most derived class calls VBase directly
class VDiamond : public VA, public VB {
public:
VDiamond() : VBase(99), VA(), VB() {}
};
Overview
Multiple inheritance allows one class to simultaneously inherit from multiple base classes, combining different capabilities into a more powerful class. However, when a common ancestor is inherited through multiple paths, the "diamond inheritance problem" creates ambiguity. This is resolved with virtual inheritance, which ensures only one shared instance of the virtual base class exists across all paths. With virtual inheritance, the most derived class (the final derived class) is responsible for directly calling the virtual base class constructor. Constructor calls written in intermediate class initializer lists are ignored when a most derived class exists. Because multiple inheritance can make designs complex, a common design guideline is to use multiple inheritance only for interface classes (containing only pure virtual functions), keeping implementation to a single base class.
If you find any errors or copyright issues, please contact us.