C++ Virtual Base Class
In this page:
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 <iostream>
class Grandparent {
public:
int value = 1;
};
class ParentA : virtual public Grandparent {};
class ParentB : virtual public Grandparent {};
class Grandchild : public ParentA, public ParentB {};
int main() {
Grandchild g;
std::cout << g.value << std::endl;
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 <iostream>
class Base {
public:
int value = 10;
};
class DerivedA : virtual public Base {};
class DerivedB : virtual public Base {};
int main() {
DerivedA a;
std::cout << a.value << std::endl;
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 <iostream>
class Base {
public:
Base() { std::cout << "Base constructed" << std::endl; }
};
class ParentA : virtual public Base {};
class ParentB : virtual public Base {};
class Grandchild : public ParentA, public ParentB {
public:
Grandchild() : Base() { std::cout << "Grandchild constructed" << std::endl; }
};
int main() {
Grandchild g;
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 <iostream>
class Base {
public:
int shared = 0;
};
class ParentA : virtual public Base {};
class ParentB : virtual public Base {};
class Grandchild : public ParentA, public ParentB {};
int main() {
Grandchild g;
g.shared = 99;
std::cout << g.shared << std::endl;
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 <iostream>
class Base {
public:
~Base() { std::cout << "Base destroyed last" << std::endl; }
};
class ParentA : virtual public Base {
public:
~ParentA() { std::cout << "ParentA destroyed" << std::endl; }
};
class Grandchild : public ParentA {
public:
~Grandchild() { std::cout << "Grandchild destroyed first" << std::endl; }
};
int main() {
Grandchild g;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: