虚基类
1.虚基类的概念
在C++语言中,一个类不能被多次说明为一个派生类的直接基类,但可以不止一次地成为间接基类。这就导致了一些问题。为了方便 说明,先介绍多继承的“类格”表示法。
派生类及其基类可用一有向无环图(DAG)表示,其中的箭头表示“由派生而来”。类的DAG常称为一个“类格”。复杂类格画出来通常更容易理解。例如:
例 5-19
class L
{ public:
int next;
…
};
class A : public L
{ };
class B : public L
{ };
class C : public A, public B
{ public :
void f()
{
next=0;
}
};
这时,next有两个赋值语句next=0; 具有二义性,它是将A::next置为零,还是将B::next置为零,或者将两者都置为0,需要在函数f()中被显式的说明.
如果希望间接基类L与其派生类的关系是如下图
当在多条继承路径上有一个公共的基类,在这些路径中的某几条路经汇合处,这个公共基类就会产生多个实例。
如果只想保存这个基类的一个实例,可以将这个公共基类说明为虚拟基类或称虚基类。
它仅是简单地将关键字virtual加到基类的描述上,例如改写上述例子为例5-20
注意!!!
一个派生类的对象的地址可以直接赋给虚基类的指针,例如:
C obj;
L * ptr=&obj;
这时不需要强制类型转换,并且,一个虚基类的引用可以引用一个派生类的对象,例如:
C obj2;
L &ref=obj2;
反之则不行,无论在强制类型转换中指定什么路径,一个虚基类的指针或引用不能转换为派生类的指针或引用。例如:
C * P=(C*)(A*)ptr;
将产生编译错误。
2. 虚基类对象的初始化
虚基类的初始化与多继承的初始化在语法上是一样的,但隐含的构造函数的调用次序有点差别。
虚基类构造函数的调用次序是这样规定的:
1. 虚基类的构造函数在非虚基类之前调用。
2. 若同一层次中包含多个虚基类,虚基类构造函数按它们说明的次序调用。
3. 若虚基类由非虚基类派生,则遵守先调用基类构造函数,再调用派生类构造函数的规则。
例如 :
class X : public Y, virtual public Z
{ }
X one;
将产生如下调用次序:
Z()
Y()
X()
这里Z是X的虚基类,故先调用Z的构造函数,再调用Y的构造函数,最后才调用派生类X自己的构造函数。
# include "iostream.h"
class base
{
public:
base()
{cout<<"Base"<<endl;}
};
class base2
{
public:
base2()
{cout<<"Base2"<<endl;}
};
class level1 : public base2, virtual public base
{
public:
level1()
{cout<<"level1"<<endl;}
};
class level2 : public base2, virtual public base
{
public:
level2()
{cout<<"level2"<<endl;}
};
class toplevel : public level1, virtual public level2
{
public:
toplevel()
{cout<<"toplevel"<<endl;}
};
当建立对象view时,将产生如下调用次序:
level2()
level1()
toplevel()
而level2()要求:
base()
base2()
level2()
level1()要求
base2()
level1()
toplevel()要求
toplevel()
所以,构造函数的调用顺序为:
base()
base2()
level2()
base2()
level1()
toplevel()