Course Content
C++ OOP
C++ OOP
Diamond Inheritance
Multiple inheritance can lead to a situation known as the diamond problem or diamond inheritance, which is a significant challenge in object-oriented programming languages that support multiple inheritance.
This occurs when a subclass inherits from two or more classes, which themselves inherit from a common superclass. The term diamond is used because the inheritance scheme resembles the shape of a diamond.
main
class Base {}; class Derived1 : public Base {}; class Derived2 : public Base {}; class Diamond : public Derived1, public Derived2 {};
The primary issue with diamond inheritance is the ambiguity it creates. Since Diamond inherits from both Derived1 and Derived2, which in turn inherit from Base, there are two copies of Base within an object of Diamond. This can lead to ambiguity. For example:
main
#include <iostream> class Base { public: void display() { std::cout << "Base display()" << std::endl; } }; class Derived1 : public Base { }; class Derived2 : public Base { }; class Diamond : public Derived1, public Derived2 { }; int main() { Diamond obj; obj.display(); // Ambiguity: Which display() method should be called? }
Solution to the problem
The virtual keyword helps to avoid this problem. You can address this ambiguity through virtual inheritance, using the virtual keyword. When a Diamond is inherited virtually, C++ ensures that only one copy of the superclass is present, even if it is inherited multiple times through different paths.
main
class Base {}; class Derived1 : virtual public Base {}; class Derived2 : virtual public Base {}; class Diamond : public Derived1, public Derived2 {};
Note
Try to resolve the ambiguity issue in the previous example by utilizing virtual inheritance.
Implementing Diamond Inheritance
To implement diamond inheritance effectively:
- use the virtual keyword in the superclass declaration in the intermediate classes;
- ensure consistent use of virtual inheritance in all paths of the inheritance hierarchy;
- be mindful of constructor and destructor calls order.
Resolving Ambiguities
One of the challenges of multiple inheritance is also dealing with potential ambiguities when it comes to members with the same names.
main
#include <iostream> class Base {}; class Derived1 : public Base { public: void display() { std::cout << "Derived1 display()" << std::endl; } }; class Derived2 : public Base { public: void display() { std::cout << "Derived2 display()" << std::endl; } }; class Diamond : public Derived1, public Derived2 { }; int main() { Diamond obj; obj.display(); // Ambiguity: Which display() method should be called? }
If both superclass have members with the same name, the subclass may not know which one to use. To resolve such ambiguities, you can use the scope resolution operator (::
) to specify which base class's member you want to access. For example:
main
#include <iostream> class Base {}; class Derived1 : public Base { public: void display() { std::cout << "Derived1 display()" << std::endl; } }; class Derived2 : public Base { public: void display() { std::cout << "Derived2 display()" << std::endl; } }; class Diamond : public Derived1, public Derived2 { }; int main() { Diamond obj; obj.Derived1::display(); obj.Derived2::display(); }
Thanks for your feedback!