Virtual base classes have been introduced to eliminate something called diamond problem occurred in multiple inheritance in C++. For instance,
#include <iostream>
#include <string>
using namespace std;
class Dog{
string color;
public:
Dog(string color): color(color)
{cout<<color<<" Dog created"<<endl;}
};
class Rotweiler: public Dog{
int age;
public:
Rotweiler(int age, string color): Dog(color), age(age)
{cout<<"Age "<<age<<" "<<color<<" Rotweiler created"<<endl;}
};
class Husky: public Dog{
int teeth;
public:
Husky(int teeth, string color): Dog(color), teeth (teeth)
{cout<<"Teeth "<<teeth<<" "<<color<<" Husky created"<<endl;}
};
class MixBreed: public Rotweiler, public Husky{
public:
MixBreed(int teeth, int age, string color): Rotweiler(age,color), Husky(teeth,color)
{cout<<"Teeth "<<teeth<<" Age "<<age<<" "<<color <<" MixBreed created"<<endl;}
};
int main(){
MixBreed puppy(24,4,"black");
}
This code will result in,
black Dog created
Age 4 black Rotweiler created
black Dog created
Teeth 24 black Husky created
Teeth 24 Age 4 black MixBreed created
As you can see, we intended to create one Dog and Two others Rottweiler and Husky derived from the same Dog and finally a MixBreed derived from Rottweiler and Husky. But the compiler created two separate Dog base class objects rather deriving from the same Dog object. To address this issue, we can use the virtual inheritance.
#include <iostream>
#include <string>
using namespace std;
class Dog{
string color;
public:
Dog(string color): color(color)
{cout<<color<<" Dog created"<<endl;}
};
class Rotweiler: virtual public Dog{
int age;
public:
Rotweiler(int age, string color): Dog(color), age(age)
{cout<<"Age "<<age<<" "<<color<<" Rotweiler created"<<endl;}
};
class Husky: virtual public Dog{
int teeth;
public:
Husky(int teeth, string color): Dog(color), teeth (teeth)
{cout<<"Teeth "<<teeth<<" "<<color<<" Husky created"<<endl;}
};
class MixBreed: public Rotweiler, public Husky{
public:
MixBreed(int teeth, int age, string color): Dog(color), Rotweiler(age,color), Husky(teeth,color) // notice Dog constructor called here
{cout<<"Teeth "<<teeth<<" Age "<<age<<" "<<color <<" MixBreed created"<<endl;}
};
int main(){
MixBreed puppy(24,4,"black");
}
Notice now Rottweiler and Husky virtually inherit the class Dog. Our ultimate goal is to avoid creation of duplicate base classes. This code will result,
black Dog created
Age 4 black Rotweiler created
Teeth 24 black Husky created
Teeth 24 Age 4 black MixBreed created
But another thing to notice here is that Dog object has been created by the most derived class which is class MixBreed (you can see the Dog() constructor is called). This is because both Rotweiler and Husky are virtually inheriting Dog. When the above code compiles, the most derived class which is MixBreed should call the Dog constructor to create the one and only Dog object or else an error will result. Both Dog constructor calls from Rotweiler and Husky are ignored since Dog object has already created from the MixBreed class.
- Virtual base class objects are created at the very beginning by the compiler to avoid duplication.
- The most derived class is now responsible for creating the virtual base class object
- If you instantiate Rotweiler or Husky, they will work as normal (they will create Dog objects)
Another fact to remember is all all classes inheriting a virtual base class will have a virtual table, and they will point to the same base class object by a pointer. Therefore all derived classes from the base class can access the behaviors of the base class.