- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
类对象的内存模型的
多重继承下,对象的内存布局(按照继承的声明顺序)
有虚函数的情况下,多重继承的对象内存布局(有虚函数的基类在前,无虚函数的基类在后,最后是派生类)
虚拟继承下,对象的内存布局(被非虚拟继承的基类按照声明顺序在前,中间是派生类对象,最后是被虚拟继承的基类的基类对象)
单继承
C++ 提供继承的目的是在不同的类型之间提取共性。比如,科学家对物种进行分类,从而有种、属、纲等说法。有了这种层次结构,我们才可能将某些具备特定性质的东西归入到最合适的分类层次上,如“怀孩子的是哺乳动物”。由于这些属性可以被子类继承,所以,我们只要知道“鲸鱼、人”是哺乳动物,就可以方便地指出“鲸鱼、人都可以怀孩子”。那些特例,如鸭嘴兽(生蛋的哺乳动物),则要求我们对缺省的属性或行为进行覆盖。
C++中的继承语法很简单,在子类后加上“:base”就可以了。下面的D继承自基类C。
既然派生类要保留基类的所有属性和行为,自然地,每个派生类的实例都包含了一份完整的基类实例数据。在D中,并不是说基类C的数据一定要放在D的数据之前,只不过这样放的话,能够保证D中的C对象地址,恰好是D对象地址的第一个字节。这种安排之下,有了派生类D的指针,要获得基类C的指针,就不必要计算偏移量 了。几乎所有知名的C++厂商都采用这种内存安排(基类成员在前)。 在单继承类层次下,每一个新的派生类都简单地把自己的成员变量添加到基类的成员变量之后。 看看上图,C对象指针和D对象指针指向同一地址。
这种内存模型非常的重要,能够保证将派生类地址转为基类地址(即基类指针或引用指向与引用派生类对象)而不用计算偏移量,如果不采取这种模式,则多态中调用基类函数时,需要知道基类的地址,还得去计算偏移量。这种模式也保证了,继承时,子类可以直接在基类(这里所说的基类对象是指包含在派生类对象中的那一部分)的虚函数表中进行添加与修改,不需要另行创建一个新的虚表,虚继承就不能保证了。
多重继承
大多数情况下,其实单继承就足够了。但是,C++为了我们的方便,还提供了多重继承。
比如,我们有一个组织模型,其中有经理类(分任务),工人类(干活)。那么,对于一线经理类,即既要从上级经理那里领取任务干活,又要向下级工人分任务的角色来说,如何在类层次中表达呢?单继承在此就有点力不胜任。我们可以安排经理类先继承工人类,一线经理类再继承经理类,但这种层次结构错误地让经理类继承了工人类的属性和行为。反之亦然。当然,一线经理类也可以仅仅从一个类(经理类或工人类)继承,或者一个都不继承,重新声明一个或两个接口,但这样的实现弊处太多:多态不可能了;未能重用现有的接口;最严重的是,当接口变化时,必须多处维护。最合理的情况似乎是一线经理从两个地方继承属性和行为——经理类、工人类。
注意,上面struct可以看做是class,是公有继承。上述就是多继承的内存布局
与单继承相同的是,F实例拷贝了每个基类的所有数据。 与单继承不同的是,在多重继承下,内嵌的两个基类的对象指针不可能全都与派生类对象指针相同:
译者注:上面那行说明C对象指针与F对象指针相同,下面那行说明E对象指针与F对象指针不同。
观察类布局,可以看到F中内嵌的E对象,其指针与F指针并不相同。正如后文讨论强制转化和成员函数时指出的,这个偏移量会造成少量的调用开销(这个很重要)
具体的编译器实现可以自由地选择内嵌基类和派生类的布局。VC++ 按照基类的声明顺序 先排列基类实例数据,最后才排列派生类数据。 当然,派生类数据本身也是按照声明顺序布局的(本规则并非一成不变 ,我们会看到,当一些基类有虚函数而另一些基类没有时,内存布局并非如此)。
上面讲到的单继承和多继承的内存布局是没有虚函数时,没有虚继承时。
虚继承
回到我们讨论的一线经理类例子。让我们考虑这种情况:如果经理类和工人类都继承自“雇员类”,将会发生什么?
如果经理类和工人类都继承自雇员类,很自然地,它们每个类都会从雇员类获得一份数据拷贝。如果不作特殊处理,一线经理类的实例将含有两个 雇员类实例,它们分别来自两个雇员基类 。 如果雇员类成员变量不多,问题不严重;如果成员变量众多,则那份多余的拷贝将造成实例生成时的严重开销。更糟的是,这两份不同的雇员实例可能分别被修改,造成数据的不一致。因此,我们需要让经理类和工人类进行特殊的声明,说明它们愿意共享一份雇员基类实例数据。
很不幸,在C++中,这种“共享继承”被称为“虚继承” ,把问题搞得似乎很抽象。虚继承的语法很简单,在指定基类时加上virtual关键字即可。
使用虚继承,比起单继承和多重继承有更大的实现开销、调用开销。回忆一下,在单继承和多重继承的情况下,内嵌的基类实例地址比起派生类实例地址来,要么地址相同(单继承,以及多重继承的最靠左基类) ,要么地址相差一个固定偏移量(多重继承
文档评论(0)