Because a class can be an indirect base class to a derived class more than once, C++ provides a way to optimize the way such base classes work. Consider the class hierarchy in Figure 9.5, which illustrates a simulated lunch line.
Figure 9.5 Simulated Lunch-Line Graph
In Figure 9.5, Queue
is the base class for both CashierQueue
and LunchQueue
. However, when both classes are combined to form LunchCashierQueue
, the following problem arises: the new class contains two subobjects of type Queue
, one from CashierQueue
and the other from LunchQueue
. Figure 9.6 shows the conceptual memory layout (the actual memory layout might be optimized).
Figure 9.6 Simulated Lunch-Line Object
Note that there are two Queue
subobjects in the LunchCashierQueue
object. The following code declares Queue
to be a virtual base class:
class Queue
{
// Member list
};
class CashierQueue : virtual public Queue
{
// Member list
};
class LunchQueue : virtual public Queue
{
// Member list
};
class LunchCashierQueue : public LunchQueue, public CashierQueue
{
// Member list
};
The virtual
keyword ensures that only one copy of the subobject Queue
is included (see Figure 9.7).
Figure 9.7 Simulated Lunch-Line Object with Virtual Base Classes
A class can have both a virtual component and a nonvirtual component of a given type. This happens in the conditions illustrated in Figure 9.8.
Figure 9.8 Virtual and Nonvirtual Components of the Same Class
In Figure 9.8., CashierQueue
and LunchQueue
use Queue
as a virtual base class. However, TakeoutQueue
specifies Queue
as a base class, not a virtual base class. Therefore, LunchTakeoutCashierQueue
has two subobjects of type Queue
: one from the inheritance path that includes LunchCashierQueue
and one from the path that includes TakeoutQueue
. This is illustrated in Figure 9.9.
Figure 9.9 Object Layout with Virtual and Nonvirtual Inheritance
Note Virtual inheritance provides significant size benefits when compared with nonvirtual inheritance. However, it can introduce extra processing overhead.
If a derived class overrides a virtual function that it inherits from a virtual base class, and if a constructor or a destructor for the derived base class calls that function using a pointer to the virtual base class, the compiler may introduce additional hidden “vtordisp” fields into the classes with virtual bases. The /vd0 compiler option suppresses the addition of the hidden vtordisp constructor/destructor displacement member. The /vd1 compiler option, the default, enables them where they are necessary. Turn off vtordisps only if you are sure that all class constructors and destructors call virtual functions virtually.
The /vd compiler option affects an entire compilation module. Use the vtordisp pragma to suppress and then reenable vtordisp fields on a class-by-class basis:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )