c++类多重继承时,初始化顺序是一个基础的问题,笔者每次清楚了以后,过段时间有点含糊了,有些基础的问题,在实际开发中,用到的频率少,今天做一个总结跟大家一起分享。
这里先讨论一般的基类继承时的初始化行为,然后再讨论虚基类的情况。
例子1:
#include<iostream>
using namespace std;
class CBase
{
private:
int a;
public:
CBase(int va):a(va)
{
cout<<"CBase:"<<endl<<this->a<<endl;
}
};
class A:public CBase
{
private:
int t;
public:
A(int vt,int ut):t(vt),CBase(ut)
{
cout<<"A:"<<endl<<this->t<<endl;
}
};
class B:public CBase
{
private:
int t;
public:
B(int vt,int ut):t(vt),CBase(ut)
{
cout<<"B:"<<endl<<this->t<<endl;
}
};
class C:public B,public A
{
private:
int t;
public:
//C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),CBase(kt)
C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),t(kt)
{
cout<<"C:"<<endl<<this->t<<endl;
}
};
int main()
{
C cc(1,2,3);
return 0;
}
输出结果:
注意类C的继承顺序和构造函数初始化顺序,蓝色和红色字体,刚好是相反的,从输出结果可以看到,是一继承顺序为依据的,先调用B的构造函数,B的构造函数执行前又先调用CBase基类的构造函数,然后是类成员的初始化,最后是类构造函数体部分的执行,以此类推,总结下顺序:
1、根据继承顺序,父类构造函数,回退到基类先开始
2、类成员初始化
3、构造函数体部分初始化
另外有一点需要注意的是,C类(红色字体部分)如果用注释掉的语句来初始化,会报错的,这里就引入了虚基类的问题,类似这种CBase类作为A和B类的共同基类,而C类又继承A和B,C++类在继承的时候,存在一个膨胀的问题的,所以才会有一种设计模式“尽量用聚合或者组合的方式”实现,而不是首先考虑继承,继承的层级过深,在性能和资源的占用上是需要衡量的,虚基类正好是可以解决这个问题的,今天重点讨论的是,虚基类继承的初始化行为,看下面的例子2:
#include<iostream>
using namespace std;
class CBase
{
private:
int a;
public:
CBase(int va):a(va)
{
cout<<"CBase:"<<endl<<this->a<<endl;
}
};
class A:virtual public CBase
{
private:
int t;
public:
A(int vt,int ut):t(vt),CBase(ut)
{
cout<<"A:"<<endl<<this->t<<endl;
}
};
class B:virtual public CBase
{
private:
int t;
public:
B(int vt,int ut):t(vt),CBase(ut)
{
cout<<"B:"<<endl<<this->t<<endl;
}
};
class C:public B,public A
{
private:
int t;
public:
C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),CBase(kt),t(kt)
//C(int vt,int ut,int kt):A(vt,ut),B(vt,ut),t(kt)
{
cout<<"C:"<<endl<<this->t<<endl;
}
};
int main()
{
C cc(1,2,3);
return 0;
}
输出结果:
对比例子1的结果,是有比较大的区别的,虚基类的构造要最先执行,并且执行一次,当再次调用A类和B类的构造函数时,CBase类的构造函数不再调用执行,这也正说明了,虚基类解决了这种冗余继承时候的“膨胀”问题。
除了这个顺序不同外,其余的顺序还是一样的。