在派生类的派生列表中可以包含多个基类:
class Bear : public ZooAnimal { /* ... */ }; class Panda : public Bear, public Endangered { /* ... */ };
每个基类包含一个可选的访问修饰符。一如往常,如果访问说明符被忽略了,则关键字class对应的默认访问说明符是private,关键字struct对应的是public。
和只有一个基类继承一样,多重继承的派生列表也只能包含已经被定义过的类,而且这些类只能是final的。对于派生类能够继承的基类个数,C++没有进行特殊的规定;但是在某个给定的派生类表中,同一个基类只能出现一次。
多重继承的派生类从每个基类中继承状态
在多重继承关系中,派生类的对象含有每个基类的子对象。
派生类构造函数初始化所有基类
构造一个派生类对象将同时构造并初始化它的所有基类子对象。与从一个基类进行派生一样,多重继承的派生类的构造函数初始值也只能初始化它的直接基类。
//显示地初始化所有基类 Panda::Panda(std::string name, bool onExhibit) : Bear(name, onExhibit, "Panda"), Endangered(Endangered::critical) {} //隐式地使用Bear的默认构造函数初始化Bear子对象 Panda::Panda() : Endangered(Endangered::critical) {}
派生类的构造函数初始值列表将实参分别传递给每个直接基类。其中基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始值列表中基类的顺序无关。
一个Panda对象按照如下次序进行初始化:
- ZooAnimal是整个继承体系的最终基类,Bear是Panda的直接基类,ZooAnimal是Bear的直接基类,所以首先初始化ZooAnimal。
- 接下来初始化Panda直接基类Bear。
- 然后初始化Panda的第二个直接基类Endangered。
- 最后初始化Panda。
继承的构造函数与多重继承
在C++11新标准中,允许派生类从它的一个或多个基类中继承构造函数。但是如果从多个基类中继承了相同的构造函数(即形参列表完全相同),则程序将产生错误:
struct Base1 { Base1() = default; Base1(const std::string&); Base1(std::shared_ptr<int>); }; struct Base2 { Base2() = default; Base2(const std::string&); Base2(int); }; //错误:D1试图从两个基类中都继承D1::D1(const std::string&) struct D1 : public Base1, public Base2 { using Base1::Base1; //从Base1继承构造函数 using Base2::Base2; //从Base2继承构造函数 };
如果一个类从它的多个基类中继承了相同的构造函数,则这个类必须为该构造函数定义它自己的版本:
struct D2 : public Base1, public Base2 { using Base1::Base1; //从Base1继承构造函数 using Base2::Base2; //从Base2继承构造函数 //D2必须自定义一个接受string的构造函数 D2(const std::string &s) : Base1(s), Base2(s) {} D2() = default; //一旦D2定义了它自己的构造函数,则必须出现 };
析构函数与多重继承
和往常一样,派生类的析构函数只负责清除派生类本身分配的资源,派生类的成员及其基类都是自动销毁的。合成的析构函数体为空。
析构函数的调用顺序正好与构造函数相反,在我们的例子中,析构函数的调用顺序是~Panda、~Endangered、~Bear、~ZooAnimal。
多重继承的派生类的拷贝和移动操作
与只有一个基类的继承一样,多重继承的派生类如果定义了自己的拷贝/赋值构造函数和赋值运算符,则必须在完整的对象上执行拷贝、移动或赋值操作。只有当派生类使用的是合成版本的拷贝、移动或赋值成员时,才会自动对其基类部分执行这些操作。在合成的拷贝控制成员中,