多重继承的复杂


继承本身就会令事情复杂化,多重继承更是会令复杂度加剧,〈虚拟继承〉中看到的不过是部份情况。

同名的方法或值域若在子类中可见,就必须处理名称重叠时的相关议题(在子类中不可见的值域或方法,代码编写上本来就不能访问,也就不会有名称重叠的判断问题)。例如,如果继承的父类有实现方法,而另一父类有同名的纯virtual函数,从父类继承的实现方法并不被视为实现了纯virtual函数。例如:

#include <iostream>
using namespace std;

class P1 {
public:
    void foo() {
        cout << "foo" << endl;
    }
};

class P2 {
public:
    virtual void foo() = 0;
};

class C : public P1, public P2 {

};

int main() { 
    C c; // error: cannot declare variable 'c' to be of abstract type 'C'

    return 0;
}

C仍被视为抽象类,C必须重新定义foo,才可用来创建实例。

如果继承的父类具有同名的实现方法,会造成实例调用的方法版本模棱两可:

#include <iostream>
using namespace std;

class P1 {
public:
    virtual void foo() {
        cout << "P1:foo" << endl;
    }
};

class P2 {
public:
    virtual void foo() {
        cout << "P2:foo" << endl;
    }
};

class C : public P1, public P2 {

};

int main() { 
    C c;

    c.foo();   // error: request for member 'foo' is ambiguous

    return 0;
}

如果C只想保留其中一个版本,那就在C中重新定义foo,并以::指明会调用哪个父类的版本,如果不想重新定义foo,那就必须明确指定给P1P2类型:

C c;

P1 &p1 = c;
p1.foo();    // 显示 P1:foo

P2 &p2 = c;
p2.foo();    // 显示 P2:foo

若从两个父类继承了同名且可见的值域,也会有类似问题:

#include <iostream>
using namespace std;

class P1 {
public:
    int x = 10;
};

class P2 {
public:
    int x = 20;
};

class C : public P1, public P2 {

};

int main() { 
    C c;

    int x = c.x; // error: request for member 'x' is ambiguous

    return 0;
}

C实例其实会有两份x,借由this地址的不同来访问,上例若要能访问,必须明确指定给P1P2类型:

C c;

P1 &p1 = c;
cout << p1.x << endl;  // 10

P2 &p2 = c;
cout << p2.x << endl;  // 20




展开阅读全文