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