Malecrab

  • 博客园
  • 首页
  • 新随笔
  • 联系
  • 订阅
  • 管理

C/C++杂记:虚函数的实现的基本原理

1. 概述

简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例:

其中:

  • B的虚函数表中存放着B::foo和B::bar两个函数指针。
  • D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz。

提示:为了描述方便,本文在探讨对象内存布局时,将忽略内存对齐对布局的影响。

2. 虚函数表构造过程

从编译器的角度来说,B的虚函数表很好构造,D的虚函数表构造过程相对复杂。下面给出了构造D的虚函数表的一种方式(仅供参考):

提示:该过程是由编译器完成的,因此也可以说:虚函数替换过程发生在编译时。

3. 虚函数调用过程

以下面的程序为例:

编译器只知道pb是B*类型的指针,并不知道它指向的具体对象类型 :pb可能指向的是B的对象,也可能指向的是D的对象。

但对于“pb->bar()”,编译时能够确定的是:此处operator->的另一个参数是B::bar(因为pb是B*类型的,编译器认为bar是B::bar),而B::bar和D::bar在各自虚函数表中的偏移位置是相等的。

无论pb指向哪种类型的对象,只要能够确定被调函数在虚函数中的偏移值,待运行时,能够确定具体类型,并能找到相应vptr了,就能找出真正应该调用的函数。

提示:本人曾在“C/C++杂记:深入理解数据成员指针、函数成员指针”一文中提到:虚函数指针中的ptr部分为虚函数表中的偏移值(以字节为单位)加1。

B::bar是一个虚函数指针, 它的ptr部分内容为9,它在B的虚函数表中的偏移值为8(8+1=9)。

当程序执行到“pb->bar()”时,已经能够判断pb指向的具体类型了:

  • 如果pb指向B的对象,可以获取到B对象的vptr,加上偏移值8((char*)vptr + 8),可以找到B::bar。
  • 如果pb指向D的对象,可以获取到D对象的vptr,加上偏移值8((char*)vptr + 8) ,可以找到D::bar。
  • 如果pb指向其它类型对象...同理...

4. 多重继承

当一个类继承多个类,且多个基类都有虚函数时,子类对象中将包含多个虚函数表的指针(即多个vptr),例:

其中:D自身的虚函数与B基类共用了同一个虚函数表,因此也称B为D的主基类(primary base class)。

虚函数替换过程与前面描述类似,只是多了一个虚函数表,多了一次拷贝和替换的过程。

虚函数的调用过程,与前面描述基本类似,区别在于基类指针指向的位置可能不是派生类对象的起始位置,以如下面的程序为例:

5. 菱形继承

本文不讨论菱形继承的情形,个人觉得:菱形继承的复杂度远大于它的使用价值,这也是C++让人又爱又恨的原因之一。

如果想要深入研究,可以参考:Itanium C++ ABI。

标签: C/C++

好文要顶 关注我 收藏该文  

malecrab
关注 - 0
粉丝 - 13

+加关注

7

0

« 上一篇:C/C++杂记:深入理解数据成员指针、函数成员指针
» 下一篇:C/C++杂记:深入虚表结构

posted @ 2016-06-09 21:06 malecrab 阅读(14097) 评论(0) 编辑 收藏

刷新评论刷新页面返回顶部

注册用户登录后才能发表评论,请 登录 或 注册,访问网站首页。

最新IT新闻:

C/C++杂记:虚函数的实现的基本原理 虚函数表相关推荐

  1. 构造函数不可以是虚函数;析构函数可以是虚函数,也可以是纯虚函数。

    构造函数不可以是虚函数:析构函数可以是虚函数,也可以是纯虚函数. 一:构造函数不能声明为虚函数的原因 1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的. 而在构造 ...

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

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

  3. C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中

    C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中 测试2:证明派生类的虚函数的地址跟第一基类的虚函数地址保存在同一张虚函数表中. 派生类有多少个拥有虚函数的基类,派生类对象就有多少 ...

  4. 静态联编,动态联编,类指针之间的关系,虚函数与多态性,纯虚函数,虚析构函数

    1.静态联编,是程序的匹配,连接在编译阶段实现,也称为早期匹配.重载函数使用静态联编. 2.动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编.switch语句和if语句是动态联编的例子. #i ...

  5. c语言中什么函数可以作为虚函数,C++语言中的“虚函数”就像C语言中的指针,必须要弄懂的...

    上一节较为详细的讨论了C++语言中基类被派生类继承过程中的内存模型,尤其较为详细的分析了虚函数及其虚表.虚表指针在内存中是如何分布,如何存储的,这对于理解C++语言中的"动态绑定" ...

  6. C++虚函数的实现方式(虚表+虚指针)

    虚函数表实现原理 虚函数的实现是由两个部分组成的,虚函数指针与虚函数表. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数. 存在虚函数的类都有一个一维的虚函数表叫做虚表.每一个类 ...

  7. 虚函数必须定义(纯虚函数除外)

    1. 虚函数的声明和定义 具体关于虚函数的知识不做多讲,我在定义一个抽象类时,忘了将一个虚函数声明为 纯虚函数,又没有对其定义, 导致编译报错时报错如下: undefined reference to ...

  8. c语言纯虚函数,关于c ++:纯虚函数的重载

    我通常使用纯虚拟函数来处理代码所要求的那些方法,以使其工作良好.因此,我创建接口,然后其他用户实现其派生类.派生类只有这些作为公共的虚拟函数,而一些额外的方法应该作为私有的实现,因为我的代码不调用它们 ...

  9. 【C++】多态(早期绑定、后期绑定)、抽象类(纯虚函数)、虚析构函数

    我们都知道面向对象编程的三大特征是封装.继承.多态,今天我们就来说一下其中之一的多态. 概念: 多态: 多态字面意思就是多种形态,C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同 ...

最新文章

  1. 聊聊storm的AggregateProcessor的execute及finishBatch方法
  2. easyui中的datagrid的数据加载的问题
  3. jsp的九大内置对象和四大作用域
  4. CSS:你真的会用 z-index 吗?
  5. matlab穆尔,基于matlab(矩阵实验室)的倒立摆控制系统仿真(34页)-原创力文档
  6. 俺是如何在3个月内写出博士论文的?
  7. python pdf转html代码_python将html转成PDF的实现代码(包含中文)
  8. 技术无价,“悟”有所值——UCan下午茶这一年
  9. 基于Django的网上书城系统
  10. PSP金手指下载及使用方法和图文教程
  11. css 设置背景颜色失效?
  12. 基于层次分析法(AHP)的信贷案例详解
  13. pdf所有者和计算机怎么删除,如何修改PDF文件以及如何删除一页
  14. Python运算(四)random模块secrets模块
  15. 手机控件查看工具uiautomatorviewer中一些方法
  16. 关于eclipse导入项目后架包找不到问题
  17. 【算法】并查集的运用
  18. python名字的来历_你知道Python的由来吗
  19. 1990-2020年江苏省全省人口数、户数(常住)
  20. 关于电路的竞争与冒险问题详解

热门文章

  1. MySQL 5.5主从复制(Replication)
  2. 解决浮层弹出如何加上datepicker,并且浮动在上面
  3. 1. 金融数学中的随机变分法-Wiener空间与Wiener泛函
  4. 8. An Introduction to MCMC for Machine Learning (3)
  5. 【控制】《复杂运动体系统的分布式协同控制与优化》-方浩老师-第1章-绪论
  6. 【Matlab 控制】Simulink仿真+S函数例子
  7. 2.6 动量梯度下降法-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  8. 5.11 程序示例--垃圾邮件检测-机器学习笔记-斯坦福吴恩达教授
  9. u-boot分析之编译体验(零)
  10. 关于IC工程师的VIM实际工作技巧