龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

C++ 多重继承和虚拟继承对象模型、效率分析(2)

时间:2014-08-15 02:45来源:网络整理 作者:网络 点击:
分享到:
单一继承的对象模型呈现了一种“自然多态”的形式,基类和派生类之间的转换十分自然简单。然而多重继承有多个基类,对象有多个vptr指针,对于第二

单一继承的对象模型呈现了一种“自然多态”的形式,基类和派生类之间的转换十分自然简单。然而多重继承有多个基类,对象有多个vptr指针,对于第二个或后继基类和派生类之间的转换需要地址调整,以指向完整的基类子对象。

虚拟继承中,为了记住和共享虚拟基类,需要在类中添加指向该基类的指针。从上面的虚拟继承对象模型中可以看到,虽然和单一继承有相同的类层次结构,但虚拟继承打破了单一继承的“自然多态”形式,基类和派生类之间的转换需要调整this指针的地址。如果是虚拟多重继承,则虚拟基类/后继基类和派生类之间的转换需要this指针地址调整 。

一般规则,多重继承经由指向“第二个或者后继base class”的指针(引用)来调用derived class virtual function,该操作所连带的“必要的this指针调整”操作,必须在执行期完成,也就是说offset的大小、以及吧offset加到this指针上头的那一小段程序代码,必须有编译器在某个地方插入。为了实现this指针调整引入thunk技术,所谓thunk是一小段assembly代码,用来以适当的offset值调整this指针,并跳到virtual函数去。Thunk技术允许virtual table slot继续内含一个简单的指针,因此多重继承不需要额外任何空间上的额外负担。Slots中的地址可以直接指向virtual function,也可以指向一个相关的thunk(如果需要调整this指针)。调整this指针的第二个额外负担就是,由于两中不同的可能:(1)经由derived class(或者第一个base class)调用,(2)经由第二个(或者后继)base class调用,同一个函数在virtual table中可能需要多笔对应的slots。并且在第二个或者后继base class中的虚函数表保存的是thunk代码地址。

四、 效率

通过上面第三部分的分析,多重继承和虚拟继承对象模型的较单一继承复杂的对象模型 ,造成了成员访问低效率, 表现在两个方面:对象构建时vptr的多次设定,以及this指针的调整。对于多种继承情况的效率比较如下:

情形 Vptr 设定 Data member 访问 virtual Function member 访问 效率分析
单一继承 no vptr 指针/引用/对象访问效率相同 直接访问 效率较高
单一继承 一次 指针/引用/对象访问效率相同 通过vptr和vtable访问 多态的引入,带来了设定vptr和间接访问虚函数等效率的降低
多重继承 多次 指针/引用/对象访问效率相同 通过vptr和vtable访问,通过第二或者后继base类指针访问需要调整this指针 除了单一继承效率降低的情形,调整this指针也带来了效率的降低
虚拟继承 多次 对象/指针/应用访问效率较低 通过vptr和vtable访问,访问虚基类需要调整this指针 除了单一继承效率降低的情形,调整this指针也带来了效率的降低

多态中的data member访问

    考察多态中几种继承情形的data member成员访问效率的关键是:members的offset位置在编译期是否能够确定。 如果访问的成员在编译期就可以确定下offset位置,不会带来额外的负担。

    理论上针对上面的继承类型,通过类对象访问,效率完全一样,因为成员在类中的位置在编译期是可以确定的。通过引用或者指针访问,除了一种情形,上面的继承类型效率也完全相同 。例外情形是:通过指针和引用访问虚拟基类的数据成员。因为虚拟基类在不同的继承层次中,其offset位置是变化的,并且无法通过指针或者引用类型确定指针指向对象的真实类型,所以编译期无法确定offset位置,只能在运行期通过类型信息确定。

精彩图集

赞助商链接