Virtual base class

When used in the declaration of a derived class, the keyword virtual instructs the compiler to only use the base class once regardless of the number of derived classes using it.

 

This overcomes the so called Diamond Problem, whereby the base class constructor is invoked for each derived class. This could potentially invoke many copies of the base class constructor and thus take up more memory resources than required.

 

Here we have an imaginary JamesBondSuperCar that is derived from three types of vehicle:

 

 

This example shows the problem:

#include <iostream>
#include <string>
using namespace std;

class Vehicle {
	public :
		Vehicle() {cout << "Vehicle constructor invoked" << endl ;}
	int speed ;
};

class Car : public Vehicle {
	public :
		Car(){cout << "\tCar constructor invoked" << endl ;}
};

class Submarine : public Vehicle {
	public :
		Submarine(){cout << "\tSubmarine constructor invoked" << endl ;}
};

class Jet : public Vehicle {
	public :
		Jet(){cout << "\tJet constructor invoked" << endl ;}
};

class JamesBondSuperCar : public Car, public Submarine, public Jet {
	public:
		JamesBondSuperCar(){cout << "\t\tJamesBondSuperCar constructor invoked" << endl ;}
};

int main() {

	JamesBondSuperCar miniCooper ;

	return 0;
}

Compile & Run:

Vehicle constructor invoked 

Car constructor invoked

Vehicle constructor invoked

Submarine constructor invoked

Vehicle constructor invoked

Jet constructor invoked

JamesBondSuperCar constructor invoked

 

As can be seen, the base constructor has been called three times and therefore three copies of its members are being stored. The problem is further compounded if you wanted to define the integer speed attribute, since the compiler doesn't know which speed to set and complains with an error stating that speed is ambiguous.

 

To overcome this, we use the keyword virtual for each declaration of the base class in the derived classes, as per lines 11, 16 and 21:

#include <iostream>
#include <string>
using namespace std;

class Vehicle {
	public :
		Vehicle() {cout << "Vehicle constructor invoked" << endl ;}
	int speed ;
};

class Car : virtual public Vehicle {
	public :
		Car(){cout << "\tCar constructor invoked" << endl ;}
};

class Submarine : virtual public Vehicle {
	public :
		Submarine(){cout << "\tSubmarine constructor invoked" << endl ;}
};

class Jet : virtual public Vehicle {
	public :
		Jet(){cout << "\tJet constructor invoked" << endl ;}
};

class JamesBondSuperCar : public Car, public Submarine, public Jet {
	public:
		JamesBondSuperCar(){cout << "\t\tJamesBondSuperCar constructor invoked" << endl ;}
};

int main() {

	JamesBondSuperCar miniCooper ;

	miniCooper.speed = 666 ;

	return 0;
}

Compile & Run:

Vehicle constructor invoked 

Car constructor invoked
Submarine constructor invoked
Jet constructor invoked

JamesBondSuperCar constructor invoked

 

 

Now we can see that the base constructor  has only been called once, and we can also set the speed attribute for the derived object.

Leave a Reply