C/C++杂记 虚函数的实现的基本原理(图文)

 更新时间:2016年06月11日 13:07:02   投稿:mdxy-dxy  
这篇文章主要介绍了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++性能优化背后的方法论TMAM

    详解C/C++性能优化背后的方法论TMAM

    开发过程中我们多少都会关注服务的性能,然而性能优化是相对比较困难,往往需要多轮优化、测试,属于费时费力,有时候还未必有好的效果。但是如果有较好的性能优化方法指导、工具辅助分析可以帮助我们快速发现性能瓶颈所在,针对性地进行优化,可以事半功倍
    2021-06-06
  • C语言软件spi虚拟总线中间层设计详解

    C语言软件spi虚拟总线中间层设计详解

    这篇文章主要为大家介绍了C语言软件spi虚拟总线中间层设计详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • C++实现区块链的源码

    C++实现区块链的源码

    这篇文章主要介绍了C++实现区块链的源码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • 简介C++编程中的运算符重载

    简介C++编程中的运算符重载

    这篇文章简单介绍了C++编程中的运算符重载,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C语言编写猜数游戏

    C语言编写猜数游戏

    这篇文章主要为大家详细介绍了C语言编写猜数游戏,可以自定义猜数范围和机会次数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 基于Matlab图像处理的公路裂缝检测实现

    基于Matlab图像处理的公路裂缝检测实现

    随着公路的大量投运,公路日常养护和管理已经成为制约公路运营水平提高的瓶颈,特别是路面状态采集、检测维护等工作更是对传统的公路运维模式提出了挑战。这篇文章主要介绍了如何通过Matlab图像处理实现公路裂缝检测,感兴趣的可以了解一下
    2022-02-02
  • C++中sprintf使用的方法与printf的区别分析

    C++中sprintf使用的方法与printf的区别分析

    这篇文章主要介绍了C++中sprintf使用的方法与printf的区别,实例分析了sprintf与printf的具体用法及相关注意事项,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • 基于Qt实现视频播放器的制作

    基于Qt实现视频播放器的制作

    本文主要为大家介绍了如何利用Qt中的qMediaPlayer和qvideowidget实现视频文件(avi,mp4….)的播放,并且提供进度显示,还可以通过拖动进度条来变换播放位置,感兴趣的可以尝试一下
    2022-12-12
  • C++中浅拷贝与深拷贝的详解及其作用介绍

    C++中浅拷贝与深拷贝的详解及其作用介绍

    这篇文章主要介绍了C++中浅拷贝与深拷贝的详解及其作用介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • 详解C语言之堆栈

    详解C语言之堆栈

    这篇文章主要为大家介绍了C语言的堆栈,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11

最新评论