06/20/2020
派生类会初始化所有父类的构造函数,如果没有,将会隐式调用默认构造函数,如果父类没有默认构造函数,将会编译错误
class Bear:public ZooAnimal{};
class Panda:public Bear,public Endangered /*<---派生列表!!*/{
Panda():Endangered()/*<--构造函数初始化列表*/{}
};
基类的构造顺序与派生列表中基类中出现顺序保持一致,上述例子
Panda的直接基类是Bear和Endangered,Bear的直接基类是ZooAnimal所以先是ZooAnimal,再Bear和Endangered,因为派生列表中Bear在Endangered之前,所以先是Bear,再是Endangered最后Panda类。
#include
struct A
{
A()
{
std::cout << "A class\n";
}
};
struct B {
B() {
std::cout << "B class\n";
}
};
struct C :public B, public A /*<-----构造顺序关键在派生列表*/{
C():A(),B(){
std::cout << "C class\n";
}
};
int main() {
C c; // B,A,C 的构造顺序
return 0;
}
注意1:析构的顺序和构造的顺序相反
注意2:只有当派生类使用的是合成版本的拷贝、移动或赋值成员时,才会自动对其基类部分执行这些操作
指针和引用的静态类型决定我们能够使用哪些成员,本质就是类定义完自己的数据成员和函数之后,只能使用自己的和从别人那边继承过来的东西,你不能调用派生类独有的数据成员和函数。动态类型说明符virtual,表示你可以调用派生类的函数,但是这个函数其实你自己也有一个版本,但是派生类重写了一个自己的版本,不是派生类独有函数,这是多态表现
二义性问题会引发编译错误,再VS 2019中报 “E0266 “C::x” is ambiguous “和”C2385 ambiguous access of ‘x’“
例子
struct A
{
int x=3;
void print(int d);
};
struct B {
int x = 4;
void print();
};
struct C :public B, public A {
C()
{
std::cout << x; //改正为A::x 或者B::x ,前缀限定符
print(); //<----第二种二义性错误,尽管形参不同,但是名字相同
}
};
注意:先查找名字后进行类型检查,所以报错报的名字错误
派生类间接继承多个相同的父类,默认情况下,派生类中含有继承链上每个类对应的子部分,这就会引发派生类包含对各子对象,随意为类省内存,同时为了共享我们使用虚继承
Panda直接继承Bear,Raccoon和Endangered,但是间接继承了ZooAnimal两次,所以这里可以使用虚继承。
//virtual public 顺序无所谓
class Raccoon:public virtual ZooAnimal{};
class Bear:virtual public Bear{}
class Panda:public Raccoon,public Bear,public Endangered{};
注意:虚继承不影响派生类本身,而是影响派生类的子类,所以这里影响了Panda类
在虚派生中,虚基类是由最底层的派生类初始化的。
例子:
class Raccoon:public virtual ZooAnimal{};
class Bear:virtual public Bear{}
class Panda:public Raccoon,public Bear,public Endangered
{
//显示初始化
Panda():ZooAnimal(),Raccoon(),Bear(),Endangered //最底层Panda来初始化ZooAnimal,尽管不是直接基类
{
}
};
注意:任何情况下,对象都会变为最底层的类,所以说如果有虚继承,最好每一个类都初始化虚继承的类
Panda panda; //最底层为panda
Bear bear; //这个对象最底层变为Bear了
观察派生列表,先找虚继承先构造,如果有多个虚继承,就按照顺序完成虚继承部分,再接着按照普通顺序下去
C++ Primer 第五版 第十八章讲述了本章内容