Virtual Base Classes

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 )