而试图在C更大的深度继承机制来分析++我偶然发现了下面的例子:
#include<iostream>
using namespace std;
class Base {
public:
virtual void f(){
cout << "Base.f" << endl;
}
};
class Left : public virtual Base {
};
class Right : public virtual Base{
public:
virtual void f(){
cout << "Right.f" << endl;
}
};
class Bottom : public Left, public Right{
};
int main(int argc,char **argv)
{
Bottom* b = new Bottom();
b->f();
}
以上,不知何故,编译并调用右键:: F()。 我看看有什么可以在编译器怎么回事,它的理解是有一个共享的基本对象,而右覆盖F(),但实际上,在我的理解,应该有两种方法: Left::f()
从继承Base::f()
)和Right::f()
,它覆盖Base::f()
。 现在,我认为,基于有由底部被继承两种不同的方法,都与相同的签名,应该有一个冲突。
任何人都可以解释的C ++与该情况下的交易,其说明书的细节和它如何它从低级别的透视?
在可怕的钻石有一个单一的基础上,从所述两个中间对象导出,然后将第四类型的关闭与从两种类型中的中间水平多重继承的金刚石。
你的问题似乎是多少f
功能在前面的例子中声明? 答案是一个。
让我们开始只是基类和派生的线性分级的简单示例:
struct base {
virtual void f() {}
};
struct derived : base {
virtual void f() {}
};
在这个例子中有一个单一的f
申报其中有两个覆盖, base::f
和derived::f
。 在类型的对象derived
,最终的置换器被derived::f
。 需要注意的是两个很重要的f
功能表示有多种实现单一功能。
现在,回到最初的例子,上线在右边, Base::f
和Right::f
都以同样的方式被覆盖了相同的功能。 因此,对于类型的对象Right
,最终置换器是Right::f
。 现在对于类型的最终目标Left
,最终置换器是Base::f
作为Left
没有覆盖的功能。
当金刚石是封闭的,并且因为继承是virtual
存在单个Base
对象,即声明了一个f
功能。 在继承的第二个层次, Right
将覆盖该功能与它自己的实现,这是为最派生类型最终置换器Bottom
。
你可能想看看这外面的标准,并看看如何实际上是由编译器来实现。 编译器,从而在当Base
物体它增加了一个隐藏指针vptr
到虚拟表。 虚拟表保存指针的thunk(为简单起见只是假设,表中保存指针的函数的最后overriders,[1])。 在这种情况下, Base
对象将不包含成员数据和只是一个指向具有指向功能的表Base::f
。
当Left
延伸Base
,用于创建一个新的V Left
和在虚函数表的指针被设置到的最终超控器f
在这个水平上,这是顺便Base::f
所以在两者的vtables指针(忽略蹦床)跳转到相同的实际执行情况。 当类型的对象Left
被构建,所述Base
子对象首先被初始化,然后前成员初始化Left
(如果有)的Base::vptr
指针被更新为指Left::vtable
(即存储在指针Base
是指用于定义的表Left
)。
在钻石的另一面,那就是为创建虚函数表Right
包含结束调用一个thunk的 Right::f
。 如果类型的对象Right
是要创建相同的初始化过程中会发生与Base::vptr
将指向Derived::f
。
现在,我们得到的最终目标Bottom
。 再次,针对类型生成一个虚函数表Bottom
和虚函数表,如在所有其他的情况下,包含表示单个条目f
。 编译器分析继承层次结构,并且确定Right::f
覆盖Base::f
,并且不存在等效覆盖左侧分支,因此,在Bottom
的VTable表示指针f
指Right::f
。 再次,在施工过程中Bottom
物,该Base::vptr
被更新为指Bottom
的VTable。
正如你看到的,所有四个虚函数表有一个条目f
,有一个单一的 f
在节目中,即使储存在每个虚函数表的值是不同的(最终overriders不同)。
[1]所述的thunk是一小块代码,适应this
指针如果需要的话(多重继承通常意味着它需要),然后将呼叫转发到实际倍率。 在单继承的情况下, this
指针并不需要更新,并在thunk消失,直接指向实际功能的虚函数表的条目。