C++ 多重继承和虚拟继承对象模型、效率分析
一、多态
C++多态通过继承和动态绑定实现。继承是一种代码或者功能的传承共享,从语言的角度它是外在的、形式上的,极易理解。而动态绑定则是从语言的底层实现保证了多态的发生——在运行期根据基类指针或者引用指向的真实对象类型确定调用的虚函数功能!通过带有虚函数的单一继承我们可以清楚的理解继承的概念、对象模型的分布机制以及动态绑定的发生,即可以完全彻底地理解多态的思想。为了支持多态,语言实现必须在时间和空间上付出额外的代价(毕竟没有免费的晚餐,更何况编译器是毫无感情):
1、类实现时增加了virtual table,用来存放虚函数地址;
2、类对象中增加了指向虚函数表的指针vptr,以提供runtime的链接;
3、在类继承层次的构造函数中重复设定vptr的初值,以期待指针指向对应类的virtual table;
4、在类继承层次的析构函数中重复还原vptr的初值;
5、多态发生时(base class指针调用虚函数)需要通过vptr和virtual table表调用对应函数实体,增加了 一层间接性。
第1、2两点是多态带来的空间代价,后面三点则是时间效率上的代价。
二、多重继承和虚拟继承
多重继承具有多个base class,有别于单一继承(提供了一种“自然多态”形式)。单一继承中,基类和派生类具有相同的内存地址,它们之间的转换十分自然不需要编译器的介入。但如果基类中没有虚函数而派生类中有,单一继承的自然多态被打破。这种情况下,派生类转换为基类需要编译器的介入,用以调整this指针地址。多重继承的对象模型较单一继承复杂,根源在于derived class objects和其第二或后继的base class objects之间的“非自然”关系 ,这一点可以从下面的对象模型中看到。派生类和基类之间的非自然多态引起了一个严重的问题(在虚拟继承中也存在):derived class和第二或后继base class之间的转换(不论是对象间的直接转换或者经由其所支持的virtual function机制做转换)需要调整this指针的地址,以使其指向完整正确的class object 。
虚拟继承是一种机制,类通过虚继承指出它所希望共享虚基类的状态,虚基类在派生层次中只有一份实体。相比多重继承,虚拟继承的难点在于既要识别出相同的对象部分又要维持基类和派生类之间的多态关系 。通常情况下,实现虚拟继承时编译器将对象分割为一个不变局部和一个共享局部 。不变局部中的数据,不管后继如何衍化,总是拥有固定的offset,所以这一部分数据可以被直接存取。至于共享局部,所表现的就是virtual base class subobject。这一部分的数据,其位置会因为每次的派生操作而有变化,所以它们只能被间接存取 。各家编译器实现技术之间的差异在于间接存取方法不同。一般的策略就是先安排好派生类的不变部分,然后建立共享部分。虚拟继承base class和derived class之间非自然的多态关系,它们之间相互转换时需要对this指针地址进行调整。由于对virtual base class的支持,虚拟继承带来了额外的负担和模型复杂性。
三、多重继承和虚拟继承对象模型
造成多重继承和虚拟继承较普通单一继承复杂、效率低的本质在于 对象模型内存分布的差异, 这一点从第二部分分析也可以看到。下面示例对比列出了普通单一继承、多重继承以及虚拟继承的对象模型。需要说明的是:C++标准中并没有强制规定base class members和derived class members之间的次序关系,理论上可以自由安排之,但实际上大多数编译器都会基类成员放在前面,但虚拟继承除外。下面也是这种策略,同时把vptr作为类的第一个成员。
基类Base1、Base2以及派生类DerivedSingle、DerivedMulti类定义如下:
class Base1 { public: Base1(void); ~Base1(void); virtual Base1* clone()const; protected: float data_Base1; };
class Base2 { public: Base2(void); ~Base2(void); virtual void mumble(); virtual Base2* clone()const; protected: float data_Base2; };
class DerivedSingle: public Base1 { public: DerivedSingle(void); virtual ~DerivedSingle(void); virtual DerivedSingle* clone() const; protectd: float data_DerivedSingle; };
class DerivedMulti :public Base1, public Base2 { public: DerivedMulti(void); virtual ~DerivedMulti(void); virtual DerivedMulti* clone() const; protected: float data_DerivedMulti; };
对象模型如下,虚拟继承和单一继承类结构相同,只是继承改成了虚拟继承。
单一继承:
多重继承:
虚拟继承:
为了保证memberwise复制的正确性(否则基类子对象复制给派生类时会发生错误),C++中保证“基类子对象在派生类中的原样性 ”。
- 上一篇:C++遗传算法类文件实例分析
- 下一篇:C++函数模板与类模板实例解析