C++ Hybrid Inheritance
In this page:
class Base { };
class Left : public Base { };
class Right : public Base { };
class Derived : public Left, public Right { };
What is Hybrid Inheritance?
Hybrid inheritance combines two or more inheritance patterns in a single class hierarchy — for example, mixing hierarchical inheritance for a set of siblings with multiple inheritance where one of those siblings also draws from a second, unrelated base class.
Example: What is Hybrid Inheritance?
// Include std::cout and std::cin
#include <iostream>
// Define the type Base
class Base {
// Members below can be accessed from outside the class
public:
void show() { std::cout << "Base" << std::endl; }
};
// Define the type Sibling1
class Sibling1 : public Base {};
class Sibling2 : public Base {};
class Combo : public Sibling1 {
// Members below can be accessed from outside the class
public:
void extra() { std::cout << "Combo" << std::endl; }
};
// Program execution starts in main()
int main() {
Combo c;
c.show();
c.extra();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
The Diamond Problem Intro
The diamond problem can resurface inside hybrid hierarchies whenever a class ends up inheriting from two parents that themselves share a common grandparent, producing duplicate paths back to that shared ancestor and duplicate copies of its data.
Example: The Diamond Problem Intro
#include <iostream>
class Grandparent {
public:
int value = 1;
};
class ParentA : public Grandparent {};
class ParentB : public Grandparent {};
class Grandchild : public ParentA, public ParentB {};
int main() {
Grandchild g;
// g.value would fail to compile: ambiguous, two Grandparent copies
std::cout << g.ParentA::value << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Resolving Ambiguity with Scope Resolution
Without virtual inheritance, you can still resolve ambiguity caused by a diamond by explicitly qualifying which inheritance path you mean using the scope resolution operator, telling the compiler exactly which parent's copy of a member you're referring to.
Example: Resolving Ambiguity with Scope Resolution
// Include std::cout and std::cin
#include <iostream>
// Define the type Grandparent
class Grandparent {
// Members below can be accessed from outside the class
public:
int value = 5;
};
// Define the type ParentA
class ParentA : public Grandparent {};
class ParentB : public Grandparent {};
class Grandchild : public ParentA, public ParentB {};
// Program execution starts in main()
int main() {
Grandchild g;
// Print to the console with cout
std::cout << g.ParentA::value << " " << g.ParentB::value << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Simple Hybrid Flow
When a hybrid hierarchy's inheritance paths don't actually overlap into a diamond shape, the design stays clean and predictable, letting you freely combine inheritance styles to model genuinely complex real-world relationships between types.
Example: Simple Hybrid Flow
// Include std::cout and std::cin
#include <iostream>
// Define the type Engine
class Engine {
// Members below can be accessed from outside the class
public:
void start() { std::cout << "Engine starts" << std::endl; }
};
// Define the type Wheels
class Wheels {
// Members below can be accessed from outside the class
public:
void roll() { std::cout << "Wheels roll" << std::endl; }
};
class Car : public Engine, public Wheels {};
// Program execution starts in main()
int main() {
Car car;
car.start();
car.roll();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Constructor Call Sequence
Constructor execution order in a hybrid hierarchy follows a strict, well-defined sequence, starting from the topmost base classes and working downward through every intermediate level before finally reaching the most-derived class's own constructor.
Example: Constructor Call Sequence
// Include std::cout and std::cin
#include <iostream>
// Define the type Base1
class Base1 {
// Members below can be accessed from outside the class
public:
Base1() { std::cout << "Base1" << std::endl; }
};
// Define the type Base2
class Base2 {
// Members below can be accessed from outside the class
public:
Base2() { std::cout << "Base2" << std::endl; }
};
class Derived : public Base1, public Base2 {
public:
Derived() { std::cout << "Derived" << std::endl; }
};
// Program execution starts in main()
int main() {
Derived d;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Ending up with two copies of the grandparent class when inheriting through two paths without
virtual. - Ignoring the ambiguity error when a member of the shared base is accessed.
- Not using virtual inheritance to solve the diamond problem.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: