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

C++ Virtual Base Class

A virtual base class makes sure a shared grandparent is included only once in a diamond-shaped family, like two siblings sharing one set of grandparents. It solves duplicate copies.
Syntax
cpp
class Base { };

class Left : virtual public Base { };
class Right : virtual public Base { };
class Derived : public Left, public Right { };

Why Virtual Base Classes?

When a diamond-shaped inheritance pattern exists, a naive derived class ends up with two separate copies of the shared grandparent class — one through each parent.

Marking that grandparent as a virtual base class ensures only a single, shared copy is ever created, no matter how many paths lead to it.

Example: Why Virtual Base Classes?

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 = 1;
};

// Define the type ParentA
class ParentA : virtual public Grandparent {};
class ParentB : virtual 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.value << std::endl;
	// Return 0 to signal that the program finished successfully
	return 0;
}

Declaring Virtual Base Classes

You declare a virtual base class by adding the virtual keyword during the inheritance declaration in the intermediate classes; the order in which you write virtual and the access specifier like public doesn't matter to the compiler.

Example: Declaring Virtual Base Classes

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:
	int value = 10;
};

// Define the type DerivedA
class DerivedA : virtual public Base {};
class DerivedB : virtual public Base {};

// Program execution starts in main()
int main() {
	DerivedA a;
	// Print to the console with cout
	std::cout << a.value << std::endl;
	// Return 0 to signal that the program finished successfully
	return 0;
}

Constructor Rules with Virtual Base

With a virtual base class, responsibility for calling the shared grandparent's constructor shifts all the way down to the most-derived (grandchild) class — C++ deliberately ignores any attempt by the intermediate parent classes to call that constructor themselves.

Example: Constructor Rules with Virtual Base

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:
	Base() { std::cout << "Base constructed" << std::endl; }
};

// Define the type ParentA
class ParentA : virtual public Base {};
class ParentB : virtual public Base {};

class Grandchild : public ParentA, public ParentB {
// Members below can be accessed from outside the class
public:
	Grandchild() : Base() { std::cout << "Grandchild constructed" << std::endl; }
};

// Program execution starts in main()
int main() {
	Grandchild g;
	// Return 0 to signal that the program finished successfully
	return 0;
}

Accessing Grandparent Members

Because only one instance of the virtual base class exists no matter how many derivation paths lead to it, modifying its member variables from any subclass changes that single shared copy, visible consistently through every path in the hierarchy.

Example: Accessing Grandparent Members

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:
	int shared = 0;
};

// Define the type ParentA
class ParentA : virtual public Base {};
class ParentB : virtual public Base {};
class Grandchild : public ParentA, public ParentB {};

// Program execution starts in main()
int main() {
	Grandchild g;
	g.shared = 99;
	// Print to the console with cout
	std::cout << g.shared << std::endl;
	// Return 0 to signal that the program finished successfully
	return 0;
}

Destructor Sequence

In virtual inheritance, the single shared virtual base object is destroyed last of all, after every subclass along every path has already finished running its own destructor — the reverse of the special-cased construction order.

Example: Destructor Sequence

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:
	~Base() { std::cout << "Base destroyed last" << std::endl; }
};

// Define the type ParentA
class ParentA : virtual public Base {
// Members below can be accessed from outside the class
public:
	~ParentA() { std::cout << "ParentA destroyed" << std::endl; }
};

class Grandchild : public ParentA {
public:
	~Grandchild() { std::cout << "Grandchild destroyed first" << std::endl; }
};

// Program execution starts in main()
int main() {
	Grandchild g;
	// 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. Forgetting the virtual keyword on the inheritance.
  2. Forgetting that the most derived class must call the virtual base constructor.
  3. Ambiguity when not using virtual inheritance.
Chapter Summary
  • Inheritance lets a class reuse and extend another class, with access control affecting what derived classes can see.
  • Single, multiple, multilevel, hierarchical, and hybrid inheritance describe different class relationships.
  • A virtual base class helps avoid duplication in multiple inheritance.

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.