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. 执行orachk检查数据库环境

    Exadata环境巡检需要执行专有的exachk,而普通Oracle环境可以通过执行orachk来检查集群和数据库相关健康状况. 1.使用orachk检查健康状态 使用root用户执行,期间可能需要多 ...

  2. 图像列表组件(TImageList)

    TImageList 组件是一组同样尺寸的图像列表,每一个图像由它的Index值查询. 1.TImageList组件的典型用法 图形列表用于建立和管理大量的图像,主要是配合菜单或者工具栏按钮的使用.图 ...

  3. 如何在windows端安装和配置maven

    1.下载maven 从官方下载maven,下载页面:http://maven.apache.org/download.cgi 2.安装maven maven压缩包解压到一个没有中文,空格或其他特殊字符 ...

  4. Linux-expect(以交互形式输入命令,实现交互通信)

    1.expect简介 expect是一种脚本语言,它能够代替人工实现与终端的交互,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信. 安装命令: yum install ex ...

  5. Linux 中hdparm命令使用说明——带实例

    详解Linux中hdparm命令查看硬盘信息的用法 功能说明:显示与设定硬盘的参数. 语 法:hdparm [-CfghiIqtTvyYZ][-a ][-A <0或1>][-c ][-d ...

  6. NES/FC游戏: 勇者斗恶龙2

    武器 名称 攻击力 价格 主角 王子 公主 来源 Bamboo Stick 2 - x x x Wielded by the Princess of Moonbrooke at the start o ...

  7. Zabbix 配置笔记

    Zabbix Server 安装参考 https://www.cnblogs.com/clsn/p/7885990.html 安装脚本 #!/bin/bash #clsn #设置解析 注意:网络条件较 ...

  8. 利用nssm将jar包安装为windows服务

    1.介绍 假设我们有一个spring boot程序打包成jar文件放到windows服务器运行,第一种方式jar -jar xx方式在cmd运行.这样有个缺点,被别人误关闭了咋办?服务器重启了咋办? ...

  9. 谁说docker-compose不能水平扩展容器、服务多实例?

    ❝ 虽说我已经从docker-compose走上了docker swarm的邪门歪道,目前被迫走在k8s这条康庄大道, 但是我还是喜欢docker-compose简洁有效的部署方式. ❞ 曾其何时 d ...

  10. 【Android 抓包对抗】代理检查绕过

    1. 安装apk,点进去发现一点就挂 2. apk 拖入到jadx中观察,发现多出检查,一旦满足条件就会退出 .... if (((ConnectivityManager) getSystemServ ...