PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=059)

  本文发布于 2018-04-04 17:05:21,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=059)

环境说明

  无

前言


  关于C++的多态,我就不多说了,我理解的一句话就是:一个接口,多种实现(简称:一对多)。在C++里面,多态的最重要的实现方法为继承,简单来说:就是基类提供接口,子类可以实现基类的接口,不同的子类就构成了基类接口的不同的实现。以上就是C++课本上或者C++原理上的常见的知识。

  而今天这里所说明的是多态是怎么实现的,以及实现的详细方法。我个人认为:只有了解了这些东西后,我们才不会在多态中迷失自己的方向,导致代码稀烂。

c++ 多态


多态的实现

  多态的实现是通过一个指针数组实现的。此指针数组的一般声明如下:

//虚函数表
void ** vfptr;//此ptr指向一个void * 的数组

  说明: vfptr 变量的声明是C++编译器根据:当一个带有虚函数的类定义了一个实例时,编译器会自动在此类的前4bytes(32bit 系统)添加vfptr变量来实现多态。同时编译器会生成一个void *数组来存放实际接口方法地址(其实这里就已经实现了多态,也就是说:这里就已经定义好了这个实例可以调用哪些实际接口方法,例如是来至于基类还是子类等等),也就是虚函数表(vftable),此表是放在生成的可执行文件的数据段。

  总结:这里有一些基本的习惯。

  • vfptr放在一个类的起始相关地址。
  • vftable中的方法按照继承顺序、接口声明顺序赋值。

  以上都是自己瞎扯的原理,看不懂也没有啥问题,毕竟我的语文水平不高,语言组织能力不行,直接跳过查看下面的实例分析。

多态的实现原理解析实例
class Base
{
public:
Base() {}
~Base() {}
virtual void base1(void) = 0;
virtual void base2(void) {};
virtual void base3(void) {};
int base_a = 0;
private: };
class Base1
{
public:
Base1() {}
~Base1() {}
virtual void base2(void) {};
virtual void base3(void) {};
int base_a = 0;
private: }; class Derive:public Base
{
public:
Derive() {}
~Derive() {}
virtual void base1(void) {}
virtual void base2(void) {}
int derive_a = 1;
private: };
class Derive1 :public Base
{
public:
Derive1() {}
~Derive1() {}
virtual void base1(void) {}
virtual void base2(void) {}
virtual void derive1(void) {}
virtual void derive2(void) {}
int derive_a = 2;
private: }; class Derive2:public Derive1
{
public:
Derive2() {}
~Derive2() {}
virtual void derive1(void) {} private: }; int main()
{
Base1 b1;
Derive d1;
Derive1 dd1;
Derive2 ddd1;
return 0;
}
20200306 动态绑定的补充

关于派生对象地址赋值给基类指针 并使用基类指针调用派生类函数。源码如下:

(20200326补充END)

  以下是vs2015的调试分析截图:(若只想了解一下vfptr和vftable是个什么样子,只看此图并结合代码分析足以)

友情提示:此图中关于ddd1的vfptr和我们想要的结果是不同的,原因和调试器相关,具体关于ddd1哪里有问题,若有需求,请看以下分析。

多态的实现原理解析实例(进阶版)

  同上一份代码,我们深入到汇编里面,就可以发现一些我们不知道的细节,关于这些汇编中的其他内容部分,若想了解,参考此文章:https://blog.csdn.net/u011728480/article/details/79092194。

  这里我们只分析变量ddd1的生成过程。

  用ida载入我们生成的exe文件。找到main函数中ddd1初始化入口如下:

20200306 动态绑定的补充

关于派生对象地址赋值给基类指针 并使用基类指针调用派生类函数。反汇编如下:

ddd1对象的vfptr如下:

可以发现动态绑定的时候,基类指针的vfptr已经指向了ddd1对象的vfptr。

(20200326补充END)

  这里我们跳转到call后面的地址查看Derive2的构造函数,先调用Derive1的构造函数,再给vfptr赋值。下图是构造函数汇编

  下图是vfptr的赋值内容,也就是vftable是什么样子的

  这里可以看到,其实此实例调用的是哪些方法(也就实现了多态),早已经在编译时就确定了,只是这里才赋值而已,所以可以说是动态绑定或者静态绑定。

  下图为Derive1构造函数以及vftable值:

  下图为Base构造函数以及vftable的值:

  以上总结:我们可以发现vfptr经过了多次赋值的,不同的时间段,vfptr值不一样,所以在构造实例的时候,不同时段调用同一个接口,会有不同的方法。

同时也可以得出一个结论,每个接口的实现类都有一个虚函数表,包括接口类本身,这些虚函数表在编译时就确定了,只是vfptr赋值的时候,是在程序运行时确定的。

  根据以上的分析,可以解决许多我们关于多态的疑问。

后记


  无

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

关于C++ 多态实现技术的深度解析(vfptr,vftable)的更多相关文章

  1. 华为全栈AI技术干货深度解析,解锁企业AI开发“秘籍”

    摘要:针对企业AI开发应用中面临的痛点和难点,为大家带来从实践出发帮助企业构建成熟高效的AI开发流程解决方案. 在数字化转型浪潮席卷全球的今天,AI技术已经成为行业公认的升级重点,正在越来越多的领域为 ...

  2. 深度解析SDN——利益、战略、技术、实践(实战派专家力作,业内众多专家推荐)

    深度解析SDN——利益.战略.技术.实践(实战派专家力作,业内众多专家推荐) 张卫峰 编   ISBN 978-7-121-21821-7 2013年11月出版 定价:59.00元 232页 16开 ...

  3. Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN

    http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...

  4. (转载)(收藏)OceanBase深度解析

    一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...

  5. Kafka深度解析

    本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...

  6. Unity加载模块深度解析(Shader)

    作者:张鑫链接:https://zhuanlan.zhihu.com/p/21949663来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 接上一篇 加载模块深度解析(二 ...

  7. Unity加载模块深度解析(网格篇)

    在上一篇 加载模块深度解析(一)中,我们重点讨论了纹理资源的加载性能.这次,我们再来为你揭开其他主流资源的加载效率. 这是侑虎科技第53篇原创文章,欢迎转发分享,未经作者授权请勿转载.同时如果您有任何 ...

  8. 汇顶指纹传感器GF919深度解析

    前言: 随着指纹识别技术的日益普遍,其在手机上的应用也得到了广泛关注.作为全球第一款Android正面按压指纹识别手机,魅族MX4 Pro所搭载的国产指纹识别系统可谓是赚足了眼球,这就是由汇顶科技提供 ...

  9. mybatis 3.x源码深度解析与最佳实践(最完整原创)

    mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...

  10. 蓝鲸DevOps深度解析系列(2):蓝盾流水线初体验

    关注嘉为科技,获取运维新知 前面一篇文章<蓝鲸DevOps深度解析系列(1):蓝盾平台总览>,我们总览了蓝鲸DevOps平台的背景.应用场景.特点和能力: ​ 接下来我们继续解析蓝盾平台的 ...

随机推荐

  1. (C语言)每日代码||2023.12.25||strcpy()函数两个参数中的'\0'

    #include <stdio.h> #include <string.h> #define MAX 500 void test() { char arr1[] = { '1' ...

  2. Mac 和 windows上 好用的截图 工具 Snipaste

    Snipaste 官网:https://zh.snipaste.com/ ========================= 使用方法,比较简单,可以官网查看

  3. 让python程序一直在window后台进程运行

    一.让python程序后台运行 1.创建一个app.py文件,如 while 1: print(123)2.创建一个set_py.bat文件,里面写 python app.py3.创建一个start_ ...

  4. ASCII编码的诞生:解决字符标准化与跨平台通信的需求

    在计算机的发展过程中,字符的表示和传输一直是一个重要的问题.为了实现字符的标准化和跨平台通信,ASCII(American Standard Code for Information Intercha ...

  5. Keil MDK STM32系列(十) Ubuntu下的PlatformIO开发环境

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  6. 多模式匹配的Trie实现

    业务场景 这种需求一般用于敏感词过滤等场景, 输入是大文本, 需要快速判断是否存在匹配的模式串(敏感词), 或者在其中找出所有匹配的模式串. 对于模式串数量不超过5000的场景, 直接用暴力查找速度也 ...

  7. 【Unity3D】Unity3D技术栈

    1 前言 ​ 本文梳理了笔者在学习 Unity3D 的过程中,对 Unity3D 的理解和学习路线,以帮助读者循序渐进地学习 Unity3D,后续笔者仍会持续更新 Unity3D 相关技术栈,并同步到 ...

  8. String - 一些测试(持续更新)

    void main() { char *buffer = new char(1000); memset(buffer, 0, 1000); char buffer1[1000] = {}; buffe ...

  9. Vue框架设计:性能权衡的艺术

    "框架设计里到处都体现了权衡的艺术." 当我们设计一个框架的时候,框架本身的各个模块之间并不是相互独立的,而是相互关联.相互制约的.因此作为框架设计者,一定要对框架的定位和方向拥有 ...

  10. WPF性能优化:性能分析工具

    在硬件性能不断提升的现在,软件性能依旧是开发人员关注的重点.不同类型的程序关注的具体性能指标有所不同,服务器程序注重吞吐量,游戏引擎追求渲染效率,桌面程序则关注内存消耗以及界面加载效率和流畅性.当我们 ...