单继承易于实现。例如,在C中,继承可以模拟为:
struct Base { int a; }
struct Descendant { Base parent; int b; }
但是对于多重继承,编译器必须在新构造的类中安排多个父项。怎么做?
我看到的问题是:父母应该安排在AB或BA中,还是以其他方式安排?然后,如果我做演员:
SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;
编译器必须考虑是否更改原始指针。虚拟化需要类似的棘手事项。
答案 0 :(得分:10)
C ++创建者的以下文章描述了多重继承的可能实现:
Multiple Inheritance for C++ - Bjarne Stroustrup
答案 1 :(得分:5)
关于如何在VC ++中实现它有this pretty old MSDN article。
答案 2 :(得分:5)
然后,如果我做演员:
SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;
编译器必须考虑是否更改原始指针。虚拟的类似棘手的东西。
使用非虚拟继承,这不像你想象的那么棘手 - 在编译强制转换时,编译器知道派生类的确切布局(毕竟,编译器完成了布局)。通常所有发生的事情都是从派生类指针中添加/减去固定偏移量(对于其中一个基类可能为零)。
对于虚拟继承,它可能有点复杂 - 它可能涉及从vtbl(或类似)获取偏移量。
Stan Lippman的书"Inside the C++ Object Model"非常好地描述了这些东西可能(通常确实如此)的作用。
答案 3 :(得分:1)
父母按照他们指定的顺序排列:
class Derived : A, B {} // A comes first, then B
class Derived : B, A {} // B comes first, then A
您的第二种情况是以特定于编译器的方式处理的。一种常见的方法是使用大于平台指针大小的指针来存储额外的数据。
答案 4 :(得分:1)
这是一个有趣的问题,实际上不是C ++特有的。当你的语言具有多个调度以及多重继承(例如CLOS)时,事情也变得更加复杂。
人们已经注意到有不同的方法可以解决这个问题。您可能会在此上下文中阅读有关元对象协议(MOP)的一些内容......
答案 5 :(得分:0)
完全取决于编译器是如何完成的,但我相信它通常是通过vtable的层次结构完成的。
答案 6 :(得分:0)
我进行了简单的实验:
class BaseA { int a; };
class BaseB { int b; };
class Descendant : public BaseA, BaseB {};
int main() {
Descendant d;
BaseB * b = (BaseB*) &d;
Descendant *d2 = (Descendant *) b;
printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2);
}
输出是:
Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0
很高兴认识到静态铸造并不总是意味着“在不触及内容的情况下改变类型”。 (好吧,当数据类型不相互适应时,内容也会受到干扰,但IMO的情况也不同。)