← Back to C++ Course | Chapter 9: Inheritance | Lesson 7 of 8

C++ Hybrid Inheritance

Hybrid inheritance mixes several kinds of inheritance in one family tree, like a family with many connections. It can create a diamond shape that needs careful handling.
Syntax
cpp
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?

cpp
// 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;
}

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

cpp
#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;
}

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

cpp
// 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;
}

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

cpp
// 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;
}

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

cpp
// 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;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Ending up with two copies of the grandparent class when inheriting through two paths without virtual.
  2. Ignoring the ambiguity error when a member of the shared base is accessed.
  3. Not using virtual inheritance to solve the diamond problem.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.