这是我翻译的MSDN中的一篇文章,索引为“virtual base classes”。
因为一个类可以多次作为一个派生类的非直接基类,C++提供了一种优化这种基类工作的方法。虚基类提供了一种方法,可以在使用多重继承的类层次中节省空间和避免不确定性。
每个非虚的对象包含基类中定义的数据成员的一个副本。这种复制浪费了空间,并且需要你在访问它们时指定使用哪个基类的成员。
当一个基类指定为虚基类时,可以象一个多次的非直接基类工作而不需要复制数据成员。一个单一的数据成员的副本被所有以虚基类方式使用的基类共享。
当声明一个虚基类的时候,关键字virtual出现在派生类的基类列表中。
考虑如下图的类层次,它表示一个模拟的午餐队列。
###############
在图中,Queue是CashierQueue和LunchQueue的基类;但是,当后两个类组合形成类LunchCashierQueue时,产生了以下问题:新的类包含了Queue类型的两个子对象,一个来自CashierQueue,而另一个来自LunchQueue。下图表示了内存布局的概念图(实际的内存布局可能会经过优化):
###############
注意到LunchCashierQueue对象中有两个Queue类型的子对象。以下代码声明了Queue作为虚基类:
// deriv_VirtualBaseClasses.cpp 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 }; int main() { }
关键字virtual保证了只有一个Queue子对象被包含(如下图)。
###########
对于同一个类型,一个类可以既有它的虚子对象,又有非虚的子对象。在如下图表示的条件下,可以产生这种情况。
##########
如图所示,CashierQueue和LunchQueue使用Queue作为虚基类,但是,TakeoutQueue指定Queue作为非虚的基类。而且LunchTakeoutCashierQueue有Queue类型的两个子对象:一个来自包含LunchCashierQueue的继承路径,一个来自包含TakeoutQueue的继承路径。如下图所示:
#########
注意:和非虚继承相比,虚继承提供提供可观的空间节约优点。但是,它也引用了额外的处理开销。
如果一个派生类重定义了继承自虚基类中的一个虚函数,并且如果派生类的构造或者析构函数使用虚基类的指针来调用此函数,那么编译器可能会引入一个隐藏的名为“vtordisp”的域。/vd0编译器选项禁止生成隐藏的vtordisp构造/析构占位符。而默认的/vd1选项,只有在需要的时候生成。只有当你肯定所有的构造和虚构函数虚拟地调用虚函数时才关闭vtordisp。
/vd选项影响整个编译模式。可以对逐个类使用vtordisp预编译命令来禁止和随后打开vtordisp域。
#pragma vtordisp( off ) class GetReal : virtual public { ... }; #pragma vtordisp( on )