这里我们说说多重继承(multiple inheritance)。 多重继承的问题涉及到建构子和析构子的调用顺序问题, 多重继承造成的歧义型(例如, 当多个base classes中有同名函数的时候, 如何解决,members from common base class如何解决(dimond的时候, 即类A继承自B, C, 但是B, C又同时继承自一个类D时候, 就出现歧义了, 此时A有两份关于D的拷贝))。 当涉及到vitual base class(虚基类)的时候, 初始化的顺序和调用顺序如何呢??
首先, 定义一个class具有多个parent class 很简单, 只需要list them one by one:
<span style="font-size:14px;">class Boo: public Bum, public Foo {// specify its own properties};</span>这样, 一个Boo类的object可以upcast(向上转型)为 Bum, 或者Foo, 因为Boo继承了其父类的一切, 虽然object无法看到父类的private的部分。
当我们创建一个Boo的object的时候, 需要先调用Bum 和 Foo的default constructor, 然后在调用自己的。 也就是说,先构造父亲的那一部分, 调用析构子的顺序和调用构造子的顺序相反。
如下:
#include <iostream>using namespace std;class Sofa {public:Sofa() {cout << "构造Sofa" << endl;}~Sofa() {cout << "析构Sofa" << endl;}void sit() {cout << "sit" << endl;}};class Bed {public:Bed() {cout << "构造Bed" << endl;}~Bed() {cout << "析构Bed" << endl;}void lie() {cout << "lie" << endl;}};class Sofabed: public Bed, public Sofa { //按照list的顺序构建,, 即继承的顺序public:Sofabed() {cout << "构造Sofabed" << endl;}~Sofabed() {cout << "析构Sofabed" << endl;}void sitAndLie() {cout << "sit and lie" << endl;}};int main() {Sofabed myfur;cout << endl;myfur.sit(); //继承的myfur.lie(); //继承的myfur.sitAndLie(); // 自己的return 0;}运行结构如下:
可见, 先构造父类的, 然后才是自己的。 当有多个父亲的时候, 则按照继承的顺序构造出来。
但是, 多重继承有很多的缺点, 不建议使用。
首先, 如果有两个parent classes有same name的members的时候, 就不好了。 此时当要使用这些同名的函数的时候, 我们只能用resolution operator解决冲突。 很不好。
另外, 只使用单继承更加的easier, less prone to error, 所以最好不要使用多重继承啦。
因为多重继承可以改写为单继承。 例如Sofabed可以单继承Sofa, 而Sofa里面有一个Bed的class。
不建议使用多继承。Anyway, 我们在多说一下多重继承。
派生类(derived class)必须为每一个继承的base class的建构子提供初始化的参数。 例如如下:
<span style="font-size:14px;">Cderived::Cderived(arg_B1, arg_B2, …, argBn, arg_Cderived):B1(arg_B1), B2(arg_B2), …, Bn(arg_Bn) {// initialize Cderived's own data members}</span>注意, 基类的建构顺序是以派生类继承的顺序调用的,而不是这里的初始化列表的顺序。当派生类调用自己的defalut copy constructor的时候, 编译器(compiler)会自动的调用基类的default copy constructor。
当复制构造函数是你自己定义的时候, 你就必须把相对应的参数传递给基类的copy constructor。
<span style="font-size:14px;">Cderived::Cderived(Cdedrived &c1): B1(c1), B2(c1), …, B(c1) {// copy the rest data members}</span>多重继承造成的歧义性有如下 两种情况:
(1)如果A继承了B, 和C, 但是B, C类中定义了同一个名字的函数func(), 此时A有两个同名的函数。 解决歧义的办法是使用B::func() 和 C::func()标识, 即resolution operator。 另外, 如果我们什么基类例如B型的一个pointer ptr的时候,然后将ptr指向派生类的时候, 用ptr指向派生类A的某个成员(假如这个成员在B中也有), 此时ptr指到的不是派生类A的这个成员, 而是基类的。 因为ptr会根据自己的type(为B)去调用相关函数(尽管ptr指向的地址是A的对象)。 此时的解决办法就是把这个同时出现在A和B的成员函数声明为virtual的(虚函数)。
(2) A继承了B,和C, 但是B, C 继承了同一个D, 此时A就继承了两份D的拷贝, 形成diamond的shape。 一份来自于B, 一份拷贝来自于C. 不仅造成在调用common base class的成员函数产生的歧义性, 而且浪费了空间。 解决的办法就是使用virtual base class(虚基类)。 即将D声明为虚基类(被其他类虚继承), 这样在做common base class的时候就只有一份拷贝了。
如下:
<span style="font-size:14px;">class CA { // common base classpublic:int x;CA(int a = 0) { x = a;}};clas CB: virtual public CA {public:int y;CB(int a, int b= 0): CA(a) { y = b}; // 注意CA(a)不能省略, 下同}class CC: virtual public CA {public:int z;CC(int a = 0, int b = 0): CA(a) { z = b;}};class CD: public CB, public CC {public:int w;CD(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0):CA(a), CB(a, b), CC(c, d) { // 注意只有CA(a)对x的设置有效, 后面的CB,CC无效w = e;}};</span>下面设置的时候:
<span style="font-size:14px;">CD obj(5, 4, 3, 2, 1);// x 为5, 后面的设置就无效</span>宣告为虚基类的时候, CA的构造函数值执行了一次。
多对自己说“我能行,我一定可以”,