在《浅谈多态——概念描述》一文中,提到多态的本质就是“将子类类型的指针赋值给父类类型的指
针”。那么,为什麽这种赋值是允许的,或者说是安全的呢?反过来行不行?虚函数的动态绑定是如何实
现的呢?这些问题都将在本文得到解答。假设有如下代码(Object Pascal语言描述):T1 = classprivatemember1 : integer;publicfunction func1 : Integer; virtual;function func2 : Integer; virtual;function func3 : Integer; virtual;end;T2 = class(T1)privatemember2 : integer;publicfunction func1 : Integer; override;function func2 : Integer; override;end;最终结果是,T1类的实例的内存分布图如下(仅说明原理,并不表示编译器一定也是如此实现):___________________         ________________|     vptr        |-------> |    T1.func1  ||     member1     |         |    T1.func2  |~~~~~~~~~~~~~~~~~~~         |    T1.func3  |~~~~~~~~~~~~~~~~其中,vptr是编译器自动加入的一个成员指针(称为虚指针)。只有存在虚函数或动态函数或纯虚函
数的类才会被编译器加入这个成员指针,该指针指向一个称为“虚函数表”(Object Pascal中成为“虚
方法表”——VMT)的内存区域。虚函数表中,保存了每一个虚函数的入口地址。T2类的实例的内存分布图如下:___________________         ________________|    vptr         |-------> |    T2.func1  ||    member1      |         |    T2.func2  ||    member2      |         |    T1.func3  |~~~~~~~~~~~~~~~~~~~         ~~~~~~~~~~~~~~~~从图中我们可以知道,子类对象所占的空间大于父类对象所占空间。因此,当发生将子类类型的指针
赋值给父类类型的指针的赋值时(即所谓的“向上映射”),也就是父类类型的指针指向了子类类型的对
象所占的内存空间,那么,很显然,可以保证父类类型指针的可访问范围都是有效,所以这种“向上映
射”是绝对安全的(所谓“向上”是指类层次的上下关系,父类在上,子类在下)。这种赋值是得到编译
器认可的。也可以很容易得出结论,“向下映射”则未必安全(除非程序员真正知道指针所指对象的实际类
型)。因此,这种赋值是不被编译器允许的,当然,程序员可以通过类似 T1(Obj) 的形式进行强制类型
转换,但这种强制类型转换很不安全(可以发生在任何类和类之间),Object Pascal推荐使用 as 算符
进行类型之间的转换,如: (Obj as T1),使用 as 算符,编译器会检查对象类型和目标类型是否相容。
如果相容,转换被允许,否则编译出错。接着,我们看看虚函数的动态绑定是如何实现的。先看如下代码:procedure Test;var O : T1;beginO := T2.Create;O.func1;O.func3;O.Free;end;看着上面的内存布局图,当执行 O := T2.Create; 后,一个 T1 类型的指针指向 T2 实体。执行
O.func1 时,编译器通过 vptr 找到虚函数表,在虚函数表中定位到了 T2.func1(由于 T1.func1 被
“覆盖”了,因此虚函数表中找不到 T1.func1),于是,T2.func1 被调用,这就是动态绑定!但由于
T2 没有重写 func3,因此 O.func3 将调用 T1.func3,这一点在虚函数表中也可以很明显看出来。好了,说到这里,我想动态绑定已经说的非常清楚了,说明一点,本文虽然以 Object Pascal代码为
例,但其原理对于 C++也同样有效。C++与Object Pascal(甚至不同C++编译器之间)的区别仅在于类成
员及vptr在内存中分布的位置而已。那么,最后再谈一下 Object Pascal 独有的 DMT(动态方法表)吧。在VMT中,我们看到,子类的虚
函数表完全继承了父类的虚函数表,只是将被覆盖了的虚函数的地址改变了。每个子类都有一份自己的虚
函数表,可以想象,随着类层次的扩展,如果类层次非常深,或者子类的数量非常多的话,虚函数表将称
为占用内存量非常大的东西(即所谓的“类爆炸”)。为了防止这种情况, Object Pascal 引入了
DMT。对于程序员来说,区别仅在于使用“dynamic”关键字代替“virtual”关键字,所实现的功能也完
全一样。如果把本文开头的那段代码重写如下(用 dynamic 代替 virtual):T1 = classprivatemember1 : integer;publicfunction func1 : Integer; dynamic;function func2 : Integer; dynamic;function func3 : Integer; dynamic;end;T2 = class(T1)privatemember2 : integer;publicfunction func1 : Integer; override;function func2 : Integer; override;end;那么,T1 的内存分布图没有改变,而 T2 实例的就不一样了:___________________         ________________|    dptr         |-------> |    T2.func1  ||    member1      |         |    T2.func2  ||    member2      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~可以看到,在 T2 的动态方法表中,没有被覆盖的 T1.func3 消失了。因此:procedure Test;var O : T1;beginO := T2.Create;O.func3;O.Free;end;O.func3 这一句代码将被编译器做更多的处理:找到 T1 类的 func3 函数的入口地址,然后再调
用。比较一下 VMT 和 DMT 的区别:VMT 中的虚函数非常齐全,因此对每个虚函数的入口地址只需要简单的 [vptr + n] 的运算即可得
到,但是 VMT 容易消耗内存(有冗余)。而 DMT 比较节省空间,但要定位到没有被覆盖的函数的入口地
址时,将非常耗费时间。一般情况下,几乎每个子类都要覆盖的函数/方法,就将它声明为 virtual;如果类层次很深,或子
类很多,但某个函数/方法只被很少的子类覆盖,就将它声明为 dynamic。当然,具体就需要自己把握来
选择了。  

转载于:https://www.cnblogs.com/keycode/archive/2010/10/15/1852386.html

再谈多态——向上映射及VMT/DMT(转)相关推荐

  1. 再谈多态——向上映射及VMT/DMT

    再谈多态--向上映射及VMT/DMT 作者:Nicrosoft(nicrosoft@sunistudio.com) 2001.10.9 个人主页:http://www.sunistudio.com/n ...

  2. 第三次学JAVA再学不好就吃翔(part35)--多态向上转型和向下转型

    学习笔记,仅供参考 文章目录 面向对象 多态 多态向上转型和向下转型 多态的好处和弊端 面向对象 多态 多态向上转型和向下转型 我们首先通过以下代码复习一下自动类型提升和强制类型转换: class B ...

  3. 再谈编程范式-程序语言背后的思想

    link link 编程范式 托马斯.库尔提出"科学的革命"的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三个方面,以OOP ...

  4. 再谈编程范式—程序语言背后的思想

    编程范式 托马斯.库尔提出"科学的革命"的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三个方面,以OOP为例: 1,学科的逻 ...

  5. 再谈HTTP2性能提升之背后原理—HTTP2历史解剖

    即使千辛万苦,还是把网站升级到http2了,遇坑如<phpcms v9站http升级到https加http2遇到到坑>. 因为理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 ...

  6. python基础教程第三版怎么样-Python基础教程(第三版)(七)再谈抽象

    菜鸡的学习笔记. 7.1 对象魔法 多态:可对不同类型的对象执行相同的操作,但是操作将随对象所属的类型而异: 封装:对外隐藏对象内部工作原理的细节: 继承:可基于通用类创建出专用类. 按作者的意思,多 ...

  7. 【原创】Performanced C++ 经验规则 第五条:再谈重载、覆盖和隐藏

    第五条:再谈重载.覆盖和隐藏 在C++中,无论在类作用域内还是外,两个(或多个)同名的函数,可能且仅可能是以下三种关系:重载(Overload).覆盖(Override)和隐藏(Hide),因为同名, ...

  8. 【字符串算法1】 再谈字符串Hash(优雅的暴力)

    [字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述  [字符串算法1] 字符串Hash 老版原文: RK哈希(Rabin_Ka ...

  9. python基础教程第三版-Python基础教程(第三版)(七)再谈抽象

    菜鸡的学习笔记. 7.1 对象魔法 多态:可对不同类型的对象执行相同的操作,但是操作将随对象所属的类型而异: 封装:对外隐藏对象内部工作原理的细节: 继承:可基于通用类创建出专用类. 按作者的意思,多 ...

最新文章

  1. ORACLE数据库对比表结构
  2. mysql unsupported data type._数据查询Unsupported command错误
  3. Dev控件使用 - 皮肤
  4. 东北全面放开生育,数据揭秘人口和GDP和房价间联系
  5. 成绩管理单链表文件c语言,c语言学生信息管理完整.docx
  6. qt中QList使用removeAt()删除元素
  7. 网站直达上线运营,API接口开发中
  8. convertio文件转换器
  9. 陶哲轩实分析 习题 12.5.8 :度量空间中有界闭集不一定是紧集
  10. python bootstrap 中位数_【机器学习】Bootstrap详解
  11. 老路MBA商学课|第002课:比较优势|我做的比你好,就应该我做吗?
  12. 位运算实现求一个数的相反数
  13. excel 行列互换 绿色工具(怎么把行变成列,把列变成行)
  14. Mac远程控制软件有哪些?Macos好用的远程桌面连接软件推荐
  15. CentOS升级内核版本_linux升级内核版本_Redhat升级内核版本
  16. 三方协议服务器不填,三方协议档案转寄地址可以不填吗
  17. 星空主题设计理念_请星星设计理念
  18. 盘古开源丨数据大爆炸时代,云存储成为企业存储必然发展方向
  19. unity开发炉石传说系列卡牌生成代码部分代码
  20. Windows系统如何部署Rabbit和启动Rabbit服务

热门文章

  1. ros安装-Ubuntu14.04
  2. android 动态移动xy,android – 如何使用AChartEngine动态线图和X轴自动平移(滚动)?...
  3. c语言程序设计开卷考试b卷,C语言程序设计(B)试卷_杨崇联(A1).doc
  4. mongodb 安装包_MongoDB快速入门,掌握这些刚刚好!
  5. python文档生成工具_pydoc --- 文档生成器和在线帮助系统 — Python 3.9.1rc1 文档
  6. bilibili有电脑版吗_你体验过电脑版的《和平精英》吗?不用担心内存,也不用担心卡顿...
  7. 小学用计算机画画 说课,小学教师说课稿:《画画美丽的自然景色》
  8. hdu3699(不等式dfs)
  9. java中class_Java中Class对象详解
  10. zookeeper3.4.6安装