虚拟继承时派生类对象的构造和析构
大约 2 分钟
虚拟继承时派生类对象的构造和析构
在普通的继承体系中,比如A派生出B,B派生出C,则创建C对象时,在C类构造函数的初始化列表中 用B类构造函数,然后在B类构造函数初始化列表中调用A类的构造函数,即可完成对象的创建操作。但。
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(int ia)
: _ia(ia)
{
cout << "A(int)" << endl;
}
void f()
{
cout << "A::f()" << endl;
}
protected:
int _ia;
};
class B
: virtual public A
{
public:
B()
{
cout << "B()" << endl;
}
B(int ia, int ib)
: A(ia)
, _ib(ib)
{
cout << "B(int,int)" << endl;
}
protected:
int _ib;
};
class C
: public B
{
public:
C(int ia, int ib, int ic)
: B(ia, ib)
, _ic(ic)
{
cout << "C(int,int,int)" << endl;
}
void show() const
{
cout << "_ia: " << _ia << endl
<< "_ib: " << _ib << endl
<< "_ic: " << _ic << endl;
}
private:
int _ic;
};
void test()
{
C c(10, 20, 30);
c.show();
}
从最终的打印结果来看,在创建对象c的过程中,我们看到C带三个参数的构造函数执行了,同时B带两个参数的构造函数也执行了,但A带一个参数的构造函数,而是执行了函数。这与我们的预期是有差别的。如果想要得到预期的结果,我们还需要在C的构造函数初始化列表最后,显式调用A的相应构造函数。那为什么需要这样做呢?
在 C++ 中,如果继承链上存在虚继承的基类,则最底层的子类要负责完成该虚基类部分成员的构造。即我们需要显式调用虚基类的构造函数来完成初始化,如果不显式调用,则编译器会调用虚基类的缺省构造函数,不管初始化列表中次序如何,对虚基类构造函数的调用总是先于普通基类的构造函数。如果虚基类中没有定义的缺省构造函数,则会编译错误。因为如果不这样做,虚基类部分会在存在的多个继承链上被多次初始化。很多时候,对于继承链上的中间类,我们也会在其构造函数中显式调用虚基类的构造函数,因为一旦有人要创建这些中间类的对象,我们要保证它们能够得到正确的初始化。这种情况在菱形继承中非常明显, 我们接下来看看这种情况