跳至主要內容

虚拟继承时派生类对象的构造和析构

张威大约 2 分钟c/c++多态

虚拟继承时派生类对象的构造和析构

在普通的继承体系中,比如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++ 中,如果继承链上存在虚继承的基类,则最底层的子类要负责完成该虚基类部分成员的构造。即我们需要显式调用虚基类的构造函数来完成初始化,如果不显式调用,则编译器会调用虚基类的缺省构造函数,不管初始化列表中次序如何,对虚基类构造函数的调用总是先于普通基类的构造函数。如果虚基类中没有定义的缺省构造函数,则会编译错误。因为如果不这样做,虚基类部分会在存在的多个继承链上被多次初始化。很多时候,对于继承链上的中间类,我们也会在其构造函数中显式调用虚基类的构造函数,因为一旦有人要创建这些中间类的对象,我们要保证它们能够得到正确的初始化。这种情况在菱形继承中非常明显, 我们接下来看看这种情况