C++ 在虚拟继承的情况下如何处理祖父母类的成员?

C++ 在虚拟继承的情况下如何处理祖父母类的成员?,c++,inheritance,constructor,C++,Inheritance,Constructor,我在某处阅读了以下代码,作为解决多重继承情况下菱形问题的示例: #include<iostream> using namespace std; class A { int x; public: A() {} A(int i) { x = i; } void print() { cout << x; } }; class B: virtual public A { public: B():A(10) { } }; class C: v

我在某处阅读了以下代码,作为解决多重继承情况下菱形问题的示例:

#include<iostream>
using namespace std;

class A
{
int x;

public:
    A() {}
    A(int i) { x = i; }
    void print() { cout << x; }
};

class B: virtual public A
{
public:
    B():A(10) { }
};

class C: virtual public A 
{
public:
    C():A(100) { }
};

int main()
{
    D d;
    d.print();
    return 0;
}
我从印刷品中得到一些垃圾价值。如果类
D
定义如下(显式调用A的参数化构造函数):

我得到20作为输出。在第一种情况下,我可以理解调用了默认的
A()
构造函数,因此垃圾值x没有设置为任何值

然而,在第二种情况下,它并不清楚。
A(int)
的参数化构造函数何时调用?若我理解正确,调用顺序取决于继承顺序。由于
B
首先被继承,
B
的构造函数调用优先于
C
。由于
B
继承
A
,因此将首先调用
A()。然后将调用
B
的构造函数。然后将调用
C
的构造函数。最后,将调用
A(int)
,因为
A
的构造函数在类
D
中被显式调用。如果是这种情况,那么对于第二种情况,输出是合理的。但是,这与下面代码段的输出相矛盾:

#include<iostream>
using namespace std;
class Person {
public:
    Person(int x) { cout << "Person::Person(int ) called" << endl; }
    Person()     { cout << "Person::Person() called" << endl; }
};

class Faculty : virtual public Person {
public:
    Faculty(int x):Person(x) {
    cout<<"Faculty::Faculty(int ) called"<< endl;
    }
};

class Student : virtual public Person {
public:
    Student(int x):Person(x) {
        cout<<"Student::Student(int ) called"<< endl;
    }
};

class TA : public Faculty, public Student {
public:
    TA(int x):Student(x), Faculty(x), Person(x) {
        cout<<"TA::TA(int ) called"<< endl;
    }
};

int main() {
    TA ta1(30);
}

为什么在本例中
Person(int)
在开头调用,而不是在最后调用?

构造总是从基本
类开始。如果有多个基
e,则从最左边的基开始。(旁注:如果存在
虚拟继承
继承,则给予更高的优先权)。然后轮到成员字段了。它们按照声明的顺序进行初始化。最后构造
本身


析构函数的顺序与虚拟继承正好相反,虚拟类的构造函数仅在最派生的类中调用

初始化的顺序并不取决于初始化列表的顺序,而是取决于类内部声明的顺序

由于B继承A,因此首先将调用A()。然后是B 将调用构造函数

如果
A
实际上是继承的,则情况并非如此

当一个类实际上被继承时,为了调用构造函数和析构函数,它被最派生的类有效地继承。这就是虚拟继承之间的关系

由于
D
派生自
B
C
,因此在该类层次结构中
D
在调用构造函数和析构函数时继承
A
,因为
D
是派生最多的类。

N4594 12.6.2/13:

在非委托构造函数中,初始化按以下顺序进行:

  • 首先,并且仅对于派生类最多的构造函数(1.8),虚拟基类在 它们在基类的有向无环图的深度优先的从左到右遍历中出现的顺序, 其中“从左到右”是派生类基类说明符列表中基类的出现顺序
  • 然后,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化 (无论mem初始值设定人的顺序如何)
  • 然后,按照类定义中声明的顺序初始化非静态数据成员 (同样,无论mem初始值设定者的顺序如何)
  • 最后,执行构造函数主体的复合语句
[注意:声明顺序的任务是确保在 初始化的相反顺序。-结束注释]


这个答案解释了应该在哪里调用虚拟基类的构造函数。OP问虚拟基类的构造函数是什么时候调用的。@PcAF:我可能不清楚,但这是我第二段的目标。我认为类内声明(基类)的顺序不清楚。
class D: public B, public C 
    {
    public:
        D():B(),C(),A(20){}
    };
#include<iostream>
using namespace std;
class Person {
public:
    Person(int x) { cout << "Person::Person(int ) called" << endl; }
    Person()     { cout << "Person::Person() called" << endl; }
};

class Faculty : virtual public Person {
public:
    Faculty(int x):Person(x) {
    cout<<"Faculty::Faculty(int ) called"<< endl;
    }
};

class Student : virtual public Person {
public:
    Student(int x):Person(x) {
        cout<<"Student::Student(int ) called"<< endl;
    }
};

class TA : public Faculty, public Student {
public:
    TA(int x):Student(x), Faculty(x), Person(x) {
        cout<<"TA::TA(int ) called"<< endl;
    }
};

int main() {
    TA ta1(30);
}
Person::Person(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called