1、为什么要引入虚基类
如果一个派生类是从多个基类派生出来的,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性。
比如有以下结构
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="<<a<<endl;} };
class B1:public B{
public:
B1( ){ a=a+10; cout<<"B1 a="<<a<<endl;} };
class B2:public B{
public:
B2( ){ a=a+20; cout<<"B2 a="<<a<<endl;} };
class D:public B1,public B2{
public:
D( ){ cout<<"D a="<< a<<endl;} };
//将D的构造函数改成如下结构能够通过运行
// D( ){ cout<<"B1 a="<<B1:: a<<endl; cout<<"B2 a="<< B2::a<<endl;} };
int main( )
{ D obj; return 0; }
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{ . . .}
3、虚基类的初始化
虚基类的初始化与一般的多重继承的初始化在语法上基本上是一样的,但有一些特殊的规定:
对同一个虚基类的构造函数只调用一次,且是在第一次出现时调用;
修改后代码
class B {
protected:
int a;
public:
B( ){ a=5; cout<<"B a="<<a<<endl;} };
class B1:public virtual B{
public:
B1( ){ a=a+10; cout<<"B1 a="<<a<<endl;} };
class B2:public virtual B{
public:
B2( ){ a=a+20; cout<<"B2 a="<<a<<endl;} };
class D:public B1,public B2{
public:
D( ){ cout<<"D a="<< a<<endl;} };
int main( )
{ D obj; return 0; }
运行结果文章来源:https://www.toymoban.com/news/detail-477789.html
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"<<endl; } };
class B1:virtual public B{ int b;
public:
B1(int sa,int sb):B(sa)
{ b=sb; cout<<"Constructing B1"<<endl; } };
class B2:virtual public B{ int c;
public:
B2(int sa,int sc):B(sa)
{ c=sc; cout<<"Constructing B2"<<endl; } };
class D:public B1,public B2 {
int d;
public:
D(int sa,int sb,int sc,int sd): B(sa),B1(sa,sb),B2(sa,sc)
{ d=sd; cout<<"Constructing D"<<endl;} };
int main()
{ D obj(2,4,6,8); return 0; }
任何一个类的上层(不一定是直接继承虚基类),只要含有虚基类都要再构造函数中调用虚基类的构造函数。
运行结果
Constructing B
Constructing B1
Constructing B2
Constructing D
5、虚基类构造函数调用顺序
若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;文章来源地址https://www.toymoban.com/news/detail-477789.html
class X∶public Y,virtual public Z{
//…
};
X one;
定义类X的对象one后,将产生如下的调用次序。
Z( );
Y( );
X( );
到了这里,关于C++虚基类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!