- 1、为什么要引入虚基类
- 2、虚基类的概念
- 3、虚基类的初始化
- 4 关于虚基类子类构造函数
- 5 虚基类构造函数调用顺序
如果一个派生类是从多个基类派生出来的,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性。
比如有以下结构
class B{
protected: int a;
public: B( ){ ...} };
class B1: public B{
public: base1( ){...} };
class B2: public B{
public: B2( ){...} };
class D:public B1,public B2{ . . .}
以下程序会报错,因为a具有二义性。
class B {
protected:
int a;
public:
B( ){ a=5; cout<<"B a="<B a=5
B1 a=15
B a=5
B2 a=25
B1 a=15
B2 a=25
能够看到a被初始化了两次,我们希望a只被再第一次初始化以后就不再继续初始化了。那么我们要使用虚基类。
2、虚基类的概念如果将公共基类说明为虚基类。那么,对同一个虚基类的构造函数只调用一次,这样从不同的路径继承的虚基类的成员在内存中就只拥有一个拷贝。从而解决了以上的二义性问题。
继承方式
class 派生类名:继承方式 virtual 基类名
{ … }
或
class 派生类名: virtual 继承方式 基类名
{ … }
我们将上面的继承结构改成如下所示
class B{
protected: int a;
public: B( ){ ...} };
class B1: public virtual B{
public: B1( ){...} };
class B2: public virtual B{
public: B2( ){...} };
class D:public B1,public B2{ . . .}
虚基类的初始化与一般的多重继承的初始化在语法上基本上是一样的,但有一些特殊的规定:
对同一个虚基类的构造函数只调用一次,且是在第一次出现时调用;
修改后代码
class B {
protected:
int a;
public:
B( ){ a=5; cout<<"B a="<B a=5
B1 a=15
B2 a=35
D a=35
可以看到a只被初始化了一次。
4 关于虚基类子类构造函数如果在虚基类中定义有带形参的构造函数,并且没有定义默认形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调用,以初始化在虚基类中定义的数据成员。
class B { int a;
public:
B(int sa)
{ a=sa; cout<<"Constructing B"<
任何一个类的上层(不一定是直接继承虚基类),只要含有虚基类都要再构造函数中调用虚基类的构造函数。
运行结果
Constructing B
Constructing B1
Constructing B2
Constructing D
5 虚基类构造函数调用顺序
若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;
class X∶public Y,virtual public Z{
//…
};
X one;
定义类X的对象one后,将产生如下的调用次序。
Z( );
Y( );
X( );



