C++ Virtual Base Class
In this page:
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?
// 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;
}
Login to try C/C++/Java/PHP code in the editor
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
// 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;
}
Login to try C/C++/Java/PHP code in the editor
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
// 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;
}
Login to try C/C++/Java/PHP code in the editor
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
// 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;
}
Login to try C/C++/Java/PHP code in the editor
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
// 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;
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting the
virtualkeyword on the inheritance. - Forgetting that the most derived class must call the virtual base constructor.
- Ambiguity when not using virtual inheritance.
- 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.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: