从汇编分析一下下面的多态模拟结构



利用 父类指针指向子类的特性,可以间接调用各子类中的虚函数。
虽然指针类型为父类,但由于虚表的排列顺序是按虚函数在类继承层次中首次声明的顺序依次排列的,因此,只要继承了父类,其派生类的虚表中的父类部分的排列就与父类的一致,子类新定义的虚函数将会按照声明顺序紧跟其后。

我们给Speak函数传递任何一个基于CPerson的派生对象地址都能正确调用虚函数ShowSpeak,那么当我们调用虚函数时,它是如何实现调用的呢?请看下面分析

分析如下:

地址401108的eax是this指针,即对象的地址,
然后40110B是取了this指针所指的地方的值(也就是虚表指针的值),即虚表的地址传给edx,
最后edx+4也就是虚表里面第二个元素的地址,然后[edx+4]即第二个元素的值(因为数组元素是一个函数指针),也就是第二个虚函数ShowSpeak的地址。

由于虚函数采用间接调用机制,因此在使用父类指针pPerson调用虚函数时,没有依照其作用域调用CPerson类中定义的成员函数ShowSpeak

当父类中定义有虚函数时,将会产生虚表。当父类的派生类产生对象时,将会调用子类的构造函数前优先调用父类构造函数,并以子类对象的首地址作为this指针传给父类构造函数。

在父类构造函数中,会先初始化子类虚表指针为父类的虚表首地址。此时,如果在父类构造函数中调用虚函数,虽然虚表指针属于子类对象,但指向的地址却是父类的虚表首地址,这是可判断虚表所属作用域相同,于是转成直接调用,从而造成构造函数内的虚函数失效



这里也就是在构造函数里面调用了 虚函数,会出现 多态的失效。

父类构造函数会在子类构造函数之前运行,在执行父类构造函数时将虚表指针修改为当前类的虚表指针,即父类的虚表指针,导致虚函数特性失效。

如果父类构造函数内部存在虚函数调用的话,这样的顺序能防止在子类中构造父类时,父类会根据虚表错误地调用子类的成员函数。

大家心里会不会出现一个疑问:直接让编译器把构造函数或析构函数中的虚函数调用修改为直接调用方式,不就可以避免这类问题了吗????
但是大家别忘了,程序员仍然可以自己编写其它成员函数间接调用本类中声明的其它虚函数。

举个例子:
假设类A中定义了成员函数f1()和虚函数f2(),而且类B继承类A并重写f2()。根据前面的讲解我们可以知道,在子类B的构造函数执行前会调用父类A的构造函数,此时如果在类A的构造函数中调用了f1(),显然不会构成多态,编译器会直接调用f1()的代码,但是,如果在f1()中调用了f2(),此时就会产生间接调用的指令,形成多态。如果类B的对象的虚表指针没有更换为类A的虚表指针,就会导致在访问类B 的虚表后调用到类B中的f2()函数,而此时类B的对象尚未完成构造,其数据成员时不确定的,这时在f2()中引用类B的对象中的数据成员是很危险的。

同理,在析构类B的对象时,会先执行类B的析构函数,然后执行类A 的析构函数。如果在类A的析构函数中调用f1(),显然不能构成多态,编译器同样会产生直接调用f1()的代码。但是,如果f1()中又调用了f2(),此时会构成多态,如果这个对象的虚表指针没有更换为类A 的虚表指针,同样也会导致访问虚表并调用类B中的f2()。但是,此时B类对象已经执行过析构函数了,所以B类中定义的数据已经不可靠了,对其进行操作是很危险的

这也就是我们经常说的在构造函数和析构函数里面会发生虚表覆盖

析构总结:
首先调用自身的析构函数,然后调用成员对象的析构函数,最后调用父类的析构函数。
在对象析构时,首先设置虚表指针为自身虚表,再调用自身的析构函数。
如果有成员对象,则按申明顺序以 倒序方式依次调用成员对象的析构函数。
最后,调用父类析构函数。在调用父类析构函数的时候,会设置虚表指针为父类自身的虚表。

构造函数和析构函数调用流程如下:



分析如下:
首先调用了父类的构造函数,然后设置虚表指针为当前类的虚表首地址。而析构函数中的顺序却与构造函数相反,首先设置虚表指针为当前类的虚表首地址,然后调用父类的析构函数。其构造和析构过程如下:
构造:基类------>基类的派生类------->…………------->当前类
析构:当前类------>基类的派生类------->…………------->基类
虚函数系列:
详解虚函数的实现过程之初探虚表(1)
详解虚函数的实现过程之单继承(2)
详解虚函数的实现过程之多重继承(3)
详解虚函数的实现过程之虚基类(4)
详解虚函数的实现过程之菱形继承(5)

详解虚函数的实现过程之单继承(2)相关推荐

  1. 详解虚函数的实现过程之菱形继承(5)

    大家看到标题,会不会菱形继承的虚表会不会是重复的呢?祖父类的虚表会不会在子类会不会是两份相同呢?那么我们一起来探索一下吧,冲冲冲!! 首先我们来分析一下: 它一共定义了四个类,分别为CFurnitur ...

  2. 详解虚函数的实现过程之菱形继承修罗场(6)

    在这里想跟大家一起来探索一下菱形继承的类对象如何来执行构造函数,以及析构函数 这展示了子类CSofaBed的构造过程,它的特别之处是在调用时要传入一个参数,这个参数是一个标志信息.构造函数中要先构造父 ...

  3. 详解虚函数的实现过程之虚基类(4)

    博客虚函数实现过程3 时提到过虚基类,这里呢,我们来详细讲述一下: 当我们在虚函数的声明结尾处添加"=0",这种虚函数就被称为纯虚函数. 它好似一个没有实现只有声明的函数,它的存在 ...

  4. 详解虚函数的实现过程之多重继承(3)

    下面来一起探索一下多重继承时,有虚函数会怎么继承呢? 这里大家猜一下,SofaBed会占多少个字节呢? 首先我们是不是得猜一下它有几个虚表指针? 4* 4(4个int数据)+2*4(两个虚表指针)=2 ...

  5. 详解虚函数的实现过程之初探虚表(1)

    空对象它有一字节的大小,在没有任何成员变量但是却有虚函数的对象里,它的大小是四个字节,这是为什么呢? 因为含有虚函数的对象里,对象的起始地址往后四个字节其实是 一个指针,它指向了一个数组,这个数组的元 ...

  6. python中可变参数*args传入函数时的存储方式为_python 中文读法详解Python函数可变参数定义及其参数传递方式...

    Python函数可变参数定义及其参数传递方式详解 python中 函数不定参数的定义形式如下 1. func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): ...

  7. 一分钟详解initUndistortRectifyMap函数bug修复方法

    本文首发于微信公众号「3D视觉工坊」--一分钟详解initUndistortRectifyMap函数bug修复方法 在上一篇文章OpenCV中initUndistortRectifyMap函数存在bu ...

  8. python命名空间和闭包_Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】...

    本文实例讲述了Python函数基础用法.分享给大家供大家参考,具体如下: 一.什么是命名关键字参数? 格式: 在*后面参数都是命名关键字参数. 特点: 1.约束函数的调用者必须按照Kye=value的 ...

  9. 详解Scala函数也是对象的特性

    详解Scala函数也是对象的特性

最新文章

  1. android用java_原来android不是只能用java写软件
  2. LeetCode-笔记-523. 连续的子数组和
  3. LLDB+Python脚本:增强LLDB调试
  4. powerdesigner箭头如何画_用Scratch编程画几何图形:如何画多边形
  5. 有关session的登录注销的一个小例子
  6. CTO职场解惑指南系列(一)
  7. 深入理解Java虚拟机运行时数据区
  8. 14套新鲜出炉的网页图标素材下载
  9. ie不支持replaceall_继IE之后,微软又要彻底放弃这些office了
  10. A Story of One Country (Hard) CodeForces - 1181E2 (分治)
  11. 计算机丢失libcef,无法启动程序,因为计算机中丢失libcef.dll。怎么办?
  12. 蚂蚁金服实习Android岗,面试闯关记。
  13. 半导体术语-什么CIM
  14. Cisco Packet Tracer入门实验之双机互联
  15. ROS学习之error解决记录
  16. 美国大学计算机专业排名2014,2014年美国大学计算机专业排名
  17. 从微信封杀拼多多链接浅谈我是如何解决微信屏蔽封杀外部以及广告链接的
  18. 135编辑器html点击图片播放音乐,135微信编辑器怎样添加音乐 135编辑器添加音乐图文教程...
  19. pip升级失败,pip拒绝访问
  20. PMI-ACP敏捷管理认证的含金量

热门文章

  1. 成功解决ValueError: Parameter values for parameter (n_estimators) need to be a sequence.
  2. AI:人工智能概念之机器学习ML、深度学习DL、数据挖掘、知识发现、模式识别等重要领域之间比较关系结构图之详细攻略
  3. javascript闭包学习
  4. BZOJ3569: DZY Loves Chinese II(线性基构造)
  5. Mybatis Plugin(拦截器)的开发
  6. 第二阶段冲刺 第六天
  7. document对象相关信息
  8. 2.羽翼sqlmap学习笔记之MySQL注入
  9. CodeBlocks 更改 gui 程序为 命令行
  10. C++ Primer 5th笔记(chap 19 特殊工具与技术)类成员指针