[C++] 复杂继承关系中的构造函数调用顺序

虚派生的出现,一定程度上复杂了继承关系。

这篇文章主要是根据C++ Primer 18.3.5小节总结而成的。

废话不多说,让我们直奔主题。

理论准备

  • 在虚派生中,虚基类是由最底层的派生类直接在其构造函数初始值列表中初始化的,而不是往常那样一层一层递归地初始化;
  • 在虚派生中,首先初始化对象的虚基类子部分,然后才初始化直接基类;
    • 如果虚基类自身又有基类,则像往常一样虚基类的构造函数自然也会去调用其基类的构造函数;
  • 如果有多个虚基类,则按照它们在派生列表中的顺序从左到右依次构造;

示例

Q1: 当作用于一个Final对象时,构造函数和析构函数的执行次序分别是?

Q2:一个Final对象中有几个Base子对象、几个Class子对象?

Q3:Class *pc = new Final; 存在的问题?

#include <iostream>

using namespace std;

class Class {
public:
    Class() { cout << "Class()" << endl; }
    virtual ~Class() { cout << "~Class()" << endl; }
};

class Base : public Class {
public:
    Base() { cout << "Base()" << endl; }
    ~Base() override { cout << "~Base()" << endl; }
};

class D1 : virtual public Base {
public:
    D1() { cout << "D1()" << endl; }
    ~D1() override { cout << "~D1()" << endl; }
};

class D2 : virtual public Base {
public:
    D2() { cout << "D2()" << endl; }
    ~D2() override { cout << "~D2()" << endl; }
};

class MI : public D1, public D2 {
public:
    MI() { cout << "MI()" << endl; }
    ~MI() override { cout << "~MI()" << endl; }
};

class Final : public MI, public Class {
public:
    Final() { cout << "Final()" << endl; }
    ~Final() override { cout << "~Final()" << endl; }
};

int main()
{
    {
        Final f;
        cout << endl;
    }

    Base *pb;
    Class *pc;
    MI *pmi;
    D2 *pd2;

//    pb = new Class;

//    pc = new Final;

//    pmi = pb;

    pd2 = pmi;
}

示例分析

要点

  • Final对象中有两个Class子对象,其中一个是其直接基类;
  • 此外,Final通过一个菱形继承继承了一个虚基类Base,而该虚基类又继承了一个Class;

Solution

Q1: 当作用于一个Final对象时,构造函数和析构函数的执行次序分别是?

  • 虚基类Base的存在,使得Final首先去初始化Base()。
  • 当然这里,我们并没有在Final类的初始值列表中明确指出,而是使用了隐式的默认构造;
  • 但是,Base本身有一个Class基类,为此又要先去初始化Class;
  • 接下来的初始化顺序,就跟平时的初始化没什么差别了;只是别忘了还有个Class子对象要初始化;

Q2:一个Final对象中有几个Base子对象、几个Class子对象?

1个Base子对象(虚派生),2个Class子对象;

Q3:Class *pc = new Final; 存在的问题?

由于两个Class子对象的存在,导致二义性问题

猜你喜欢

转载自blog.csdn.net/sai_j/article/details/79537951