我对两个版本的代码有一个问题。 唯一的不同是在parents类之间切换virtual关键字。 发生这种事有什么原因吗?
版本一:
#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 : 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);
}
version:Person::Person(int)调用
Person::Person(int)调用
Facturn::Facturn(int)调用
Student::Student(int)调用
Ta::Ta(int)调用
版本二:
#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 : 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)调用
Facturn::Facturn(int)调用
Person::Person(int)调用
Student::Student(int)调用
Ta::Ta(int)调用
初始化类的顺序基于它们在类的基类说明符列表中声明的顺序:
这个过程对每个初始化的对象递归地重复。
对于第一个示例:
ta
是派生最多的类,因此首先初始化它的person
虚拟基对象。
person
的构造函数主体运行并打印其消息。Factory
具有一个非虚拟基类Percers
,因此它初始化自己的Percers
子对象。
person
的构造函数主体运行并打印其消息Student
不是派生最多的类,因此它没有获得自己的Person
子对象来初始化。student
的构造函数主体运行并打印其消息结果是构造函数的主体按以下顺序执行:
人
人
教员
学生
ta
对于第二个示例:
ta
是派生最多的类,因此首先初始化它的person
虚拟基对象。
person
的构造函数主体运行并打印其消息。Factor
不是派生最多的类,因此它没有获得自己的Person
子对象来初始化。factury
的构造函数主体运行并打印其消息student
有一个非虚拟基类perers
,因此它初始化自己的perers
子对象。
person
的构造函数主体运行并打印其消息结果是构造函数的主体按以下顺序执行:
人
教员
人
学生
ta
注意,在这两种情况下,都有两个person
子对象。 对于教员
和学生
共享单个人员
子对象,两者都必须从人员
虚拟继承,即:
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;
}
};
在这种情况下,逻辑是:
ta
是派生最多的类,因此首先初始化它的person
虚拟基对象。
person
的构造函数主体运行并打印其消息。Factor
不是派生最多的类,因此它没有获得自己的Person
子对象来初始化。factury
的构造函数主体运行并打印其消息Student
不是派生最多的类,因此它没有获得自己的Person
子对象来初始化。student
的构造函数主体运行并打印其消息从而导致类的构造函数主体按以下顺序执行:
人
教员
学生
ta
初始化的顺序根据继承的基本类从左到右设置。 它基本上忽略了您在构造函数列表中设置的顺序。
class TA : public Faculty, public Student {
public:
TA(int x):Student(x), Faculty(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};
将为第一种情况初始化为:
class TA : public Faculty, public Student {
public:
TA(int x):Faculty(x), Student(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};
但是虚拟基础类是先初始化的,所以我们有:
class TA : public Faculty, public Student {
public:
TA(int x):Person(x), Faculty(x), Student(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};
现在,当factury(x)
初始化时,也会初始化person(x)
(因为它是从它派生的)。 然后初始化student
,最后初始化ta
-对象本身。
您可以通过使用-wall
编译来看到初始值设定项列表中的不正确之处。
首先,要以正确的方式实现“钻石”多重继承,你的类教师和学生都必须是虚拟的人的继承。 在本例中,您只会在输出中看到一个对Person构造函数的调用。 这就是目的:只调用祖父母的构造函数一次。
您可能使用非常非限制性的编译器,因为对代码的限制性编译(例如使用VS2017)会立即报告一个错误:error C2385:ambiguous access of'person'
。 而这正是预期的行为。
附加说明:使用虚拟继承会推迟对继承类构造函数的调用,以便由它的“孙子”执行。 在您的例子中,类TA是Person的孙子,只有它调用Person的构造函数,而教师和学生不调用。