虚拟基类

由于类可以多次是间接基类为派生类, C++ 提供一种优化这种这样的基类工作。 虚拟基类提供了一种节省空间和避免在使用多重继承的类层次结构中的多义性。

每个对象在其基类包含数据成员的副本中定义。 此副本浪费空间并要求指定基类成员的哪个副本要,每次访问它们。

如果基类指定为一个虚拟基时,它可以多次作为一个间接基,而无需其数据成员的副本。 其数据成员的一个副本由将其用作虚拟 foundation 中的所有基类共享。

在声明虚拟基类, 虚拟 关键字显示在基础列表时派生类。

考虑下图中的类层次结构,声明一个模拟的午餐行。

模拟的午餐折线图

模拟的午餐排队图

在该图中, Queue 是 CashierQueue 和 LunchQueue的基类。 但是,在中,当两类组合形成 LunchCashierQueue时,出现以下问题:新类包含两个子对象从 CashierQueue 的类型 Queue,一个和其他从 LunchQueue。 下图演示概念内存布局 (物理内存布局中) 优化。

模拟的午餐行对象

模拟的午餐排队对象

请注意在 LunchCashierQueue 对象的两 Queue 子对象。 下面的代码声明 Queue 是虚拟基类:

// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};

virtual 关键字保证只 subobject Queue 的一个副本都包括 (请参见下图)。

使用虚拟基类的模拟的午餐行对象

VS 类模拟的午餐排队对象

类可以具有一个虚元素和特定类型的一个其元素。 这在下图所示的条件发生。

同一个类的虚方法或其元素

同一个类的虚拟和非虚拟组件

在该图中, CashierQueue 和 LunchQueue 使用 Queue 作为虚拟基类。 但是, TakeoutQueue 指定 Queue 作为基类,而不是虚拟基类。 因此, LunchTakeoutCashierQueue 具有类型 Queue两个子对象:一个从包含 LunchCashierQueue 的继承路径和一个从包含 TakeoutQueue的路径。 这在下图所示。

使用虚拟和非虚拟继承的对象布局

Virtual_NonvirtualInheritanceObjectLayout 图

备注

与非虚拟继承比较,虚拟继承提供有意义的大小优点。但是,这可能会导致处理开销的额外。

如果派生类重写它从虚拟基类继承的虚函数,,并且,如果一个构造函数或一个析构函数派生的基类的调用该函数使用指向虚拟基类,编译器可以引入不同的隐藏 “vtordisp”字段添加到类与虚拟基。 /vd0 编译器选项取消隐藏的 vtordisp 构造函数/析构函数偏移成员的添加。 /vd1 编译器选项,默认值,使它们是必需的位置。 请关闭 vtordisps,只有在确信所有类构造函数和析构函数实际上调用虚函数。

/vd 编译器选项影响整个编译该模块。 使用 vtordisp 杂注取消然后重新启用基于一个类由类的基类型的 vtordisp 字段:

#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )

请参见

参考

多个基类