多重继承

先来看看多重继承吧

class 

先来看构造函数

......
001917DD  pop         ecx
001917FC  mov         ecx,dword ptr [this]  ;取出this指针
001917FF  call        cFa::cFa (01913B1h)  ;调用cFa构造函数
00191804  mov         dword ptr [ebp-4],0  ;异常计数 可以忽略
0019180B  mov         ecx,dword ptr [this]  ;获取this指针
0019180E  add         ecx,4  ;this指针偏移
00191811  call        cMo::cMo (0191302h) ;调用cMo的构造函数
00191816  mov         eax,dword ptr [this]  ;获取this指针
00191819  mov         dword ptr [eax],offset cChild::`vftable' (0198B4Ch)  ;设置虚函数表
0019181F  mov         eax,dword ptr [this]  ;获取this指针
00191822  mov         dword ptr [eax+4],offset cChild::`vftable' (0198B58h)  ;设置第二个虚函数表
00191829  mov         dword ptr [ebp-4],0FFFFFFFFh
00191830  mov         eax,dword ptr [this]  ;返回this指针
......

在上面的代码,我们看出构造函数的调用顺序是根据,从左到右的继承顺序来依次调用父类函数的

在调用cFa的构造函数时,直接传递了this指针,也就是cCh的地址,而在调用cMo的时候,传递的是this指针加4个字节的地址,也就是跳过了cFa所占的空间。

那么也就是说,父类对象在子类的内存布局的顺序和构造函数的调用顺序是一样的,那么现在cCh对象的内存结构如下图:

来看看这两个父类的构造函数:

CFa:
......
001918AD  mov         eax,dword ptr [this]
001918B0  mov         dword ptr [eax],offset cFa::`vftable' (0198B34h)
001918B6  mov         eax,dword ptr [this]
......cMo:
......
0019190D  mov         eax,dword ptr [this]
00191910  mov         dword ptr [eax],offset cMo::`vftable' (0198B40h)
00191916  mov         eax,dword ptr [this]
......

过滤掉了一些无用的代码,构造函数和非多重继承无任何区别,只是将传进来的对象的虚表换成自己的虚表。

有营养的部分来了:

两个构造函数结束后,出现了两个虚表赋值,为什么是两个虚表?

因为有两个父类,当调用父类函数的时候,需要通过偏移取得父类对象,传递指针,并调用函数。

这两个虚表在本例中意义不大,因为子类没有覆盖父类的虚函数,如果有覆盖的情况出现,这两个虚表中会保存子类覆盖的虚函数,和父类未覆盖的虚函数。那么调用就很简单了,虚表偏移。

析构函数就不上代码了,和构造函数的顺序恰巧相反。

如果在main函数中添加一行如下代码会发生什么?

cMo 

编译器会找到cCh的地址,根据cCh父类中的cMo对象的偏移位置,获取到cCh父类中的cMo的地址,并返回赋值给pMo。

抽象类

上代码

class 

不说废话,直接进入子类的构造函数

;CAbstractChild 构造函数
......
00A1198F  pop         ecx
00A11990  mov         dword ptr [this],ecx
00A11993  mov         ecx,dword ptr [this]
00A11996  call        CAbstractBase::CAbstractBase (0A11474h)
00A1199B  mov         eax,dword ptr [this]  ;获取this指针
00A1199E  mov         dword ptr [eax],offset CAbstractChild::`vftable' (0A18B40h)  ;虚函数表初始化
00A119A4  mov         eax,dword ptr [this] ;返回this指针
......;CAbstractBase 构造函数
......
00A118BF  pop         ecx
00A118C0  mov         dword ptr [this],ecx
00A118C3  mov         eax,dword ptr [this]  ;获取this指针
00A118C6  mov         dword ptr [eax],offset CAbstractBase::`vftable' (0A18B34h)  ;虚函数表初始化
00A118CC  mov         eax,dword ptr [this]  ;返回this指针
......

看起来似乎和普通的继承没什么不同?

我们来看看CAbstractBase中的虚函数表(0A18B34h)指向的Show函数吧。

它的调用约定是__purecall(IDA会识别出来) ,这个函数实际上并不是CAbstractBase的Show函数,而是编译器生成一个函数,这个函数的主要作用就是调用 _amsg_exit函数来结束程序,并返回错误码 0x19。

总结

这里包含上篇文章的总结《我们聊聊继承吧,从继承的角度出发再来聊聊多态》

单类继承:

  • 在类对象占用的内存空间,只保留一份虚表指针,也就只有一个虚表
  • 虚表中各项保存了类中各虚函数的首地址
  • 构造函数先构造父类,在构造自身
  • 析构函数先析构自身,在析构父类

多重继承:

  • 在类对象所占用的内存空间中,根据继承父类的个数保存对应的虚表指针
  • 根据所保存的虚表指针的个数,对应产生相应个数的虚表
  • 转换父类指针时,需要调整到对象的首地址
  • 构造时需要调用多个父类构造函数
  • 构造时先构造继承列表中第一个父类,然后依次调用到最后一个继承的父类构造函数。
  • 析构与构造顺序相反
  • 当对象作为成员时,整个类对象的内存结构和多重继承很相似。当类中无虚函数时,整个类对象内存结构和多重继承完全一样,可按实际情况进行还原,当父类或成员对象存在虚函数,通过观察虚表指针的位置和构造函数、析构函数中填写虚表指针的数量及目标地址,来还原继承或成员关系

抽象类继承多个抽象类_多重继承?抽象类?C++的内存布局并不复杂相关推荐

  1. java抽象类例子月薪年薪程序_利用抽象类给一个有工资收入和稿费收入的小伙伴算税...

    class AbstarctClass { public static void main(String[]args) { // TODO: 用抽象类给一个有工资收入和稿费收入的小伙伴算税: Inco ...

  2. php 抽象类 继承,PHP 抽象类继承抽象类时的注意点

    原标题:PHP 抽象类继承抽象类时的注意点 抽象类继承另外一个抽象类时,抽象类中,不能重写抽象父类的抽象方法.这样的用法,可以理解为对抽象类的扩展. 下面的例子,演示了一个抽象类继承自另外一个抽象类时 ...

  3. 基类成员的public访问权限在派生类中变为_第17篇:C++继承中虚表的内存布局

    我们已经表明,非虚类的对象实例不包含虚指针,编译器在编译阶段也没有为非虚类没有构建虚表.而本篇我们会从简单的单继承链分析虚类中虚表构造过程和内存布局.这一切假定你有如下基础 对gdb调试器使用有一个比 ...

  4. 【C++拾遗】 从内存布局看C++虚继承的实现原理

    2019独角兽企业重金招聘Python工程师标准>>> 原创作品,转载请标明:http://blog.csdn.net/xiejingfa/article/details/48028 ...

  5. C++对象内存布局--⑤GCC编译器--单个虚拟继承

    C++对象内存布局--⑤GCC编译器--单个虚拟继承 测试GNU的GCC编译器在处理虚拟继承上跟VS不同的地方.派生类的虚函数表跟虚基类表合并. //GCC编译器--单个虚拟继承.cpp //2010 ...

  6. C++对象内存布局--④VS编译器--单个虚拟继承

    C++对象内存布局--④VS编译器--单个虚拟继承 在VS2005编译器下,证明单个虚拟继承的内存布局:无论有无虚函数,必然含有虚基类表指针.虚基类表中的内容为本类实例的偏移和基类实例的相对偏移值. ...

  7. java中抽象类继承抽象类_用Java中的抽象类扩展抽象类

    java中抽象类继承抽象类 示例问题 当我创建Java :: Geci抽象类AbstractFieldsGenerator和AbstractFilteredFieldsGenerator我遇到了一个不 ...

  8. python 重写抽象类编译错误_从零开始的Java之旅5.0继承、super、this、抽象类

    前言 昨天我们对Java的类与对象.封装.构造方法进行了充分讲解,今日我们学习继承.super.this.抽象类 今日内容: 三大特性--继承 方法重写 super关键字 this关键字 抽象类 继承 ...

  9. java继承和接口的优缺点_Java抽象类和接口的优缺点---总结-2

    51Testing软件测试网[P/WX?\ 解决方案二:~+j;Hw/U1{@4g0 A^9@5]p7j:Rys0既然open.close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定 ...

最新文章

  1. SAP RETAIL系统与制造业SAP系统上关于补货的配置
  2. Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序
  3. hive sqoop 分区导入_使用sqoop将hive分区表的数据导入到mysql的解决方案:shell脚本循环...
  4. mysql配置文件调优
  5. 一种新的在线学习的方法:能够克服单人多任务学习的困难!
  6. 从零开始数据科学与机器学习算法-简单感知器-05
  7. vue 中provide的用法_聊聊Vue中provide/inject的应用详解
  8. 【传智播客】Javaweb程序设计任务教程 黑马程序员 第二章 课后答案
  9. 【不了解你就OUT了】云原生基本原则
  10. Lync Server外部访问系列PART4:部署反向代理
  11. CSS 框模型( Box module )
  12. 解除ASP.net上传文件大小的限制
  13. PHP一阶段 html+css+js 练习题汇总
  14. configserver配置中心三种配置方式
  15. 这个1500个+Javascript特效代码,瞬间提升你的开发效率
  16. WPS的文档上云 是中国式服务的胜利
  17. java google 离线地图开发_如何发布google离线地图及二次开发API
  18. php生成盖章图片,印章图案生成器
  19. 计算机竖版桌面,电脑桌面竖屏了怎么办
  20. 购物足迹功能php,wordpress实现访客足迹功能

热门文章

  1. 【Scrapy】Unsupported major.minor version 52.0 [duplicate]
  2. POJ 2184 Cow Exhibition
  3. 45套精美的 ( Android, iPhone, iPad ) 手机界面设计素材和线框图设计工具
  4. Unity+MVC:实现IDependencyResolver接口需要注意的地方
  5. 计算机二级考试答题无法启动ppt,计算机二级考试中操作题常见问题之[演示文稿]...
  6. 权势二进制(51Nod-1413)
  7. Summarize to the Power of Two(CF-1005C)
  8. 打击犯罪(信息学奥赛一本通-T1386)
  9. An Easy Problem(信息学奥赛一本通-T1223)
  10. 超级楼梯(HDU-2040)