关于C++ 多态实现技术的深度解析(vfptr,vftable)
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)的更多相关文章
- 华为全栈AI技术干货深度解析,解锁企业AI开发“秘籍”
摘要:针对企业AI开发应用中面临的痛点和难点,为大家带来从实践出发帮助企业构建成熟高效的AI开发流程解决方案. 在数字化转型浪潮席卷全球的今天,AI技术已经成为行业公认的升级重点,正在越来越多的领域为 ...
- 深度解析SDN——利益、战略、技术、实践(实战派专家力作,业内众多专家推荐)
深度解析SDN——利益.战略.技术.实践(实战派专家力作,业内众多专家推荐) 张卫峰 编 ISBN 978-7-121-21821-7 2013年11月出版 定价:59.00元 232页 16开 ...
- Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN
http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...
- (转载)(收藏)OceanBase深度解析
一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...
- Kafka深度解析
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...
- Unity加载模块深度解析(Shader)
作者:张鑫链接:https://zhuanlan.zhihu.com/p/21949663来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 接上一篇 加载模块深度解析(二 ...
- Unity加载模块深度解析(网格篇)
在上一篇 加载模块深度解析(一)中,我们重点讨论了纹理资源的加载性能.这次,我们再来为你揭开其他主流资源的加载效率. 这是侑虎科技第53篇原创文章,欢迎转发分享,未经作者授权请勿转载.同时如果您有任何 ...
- 汇顶指纹传感器GF919深度解析
前言: 随着指纹识别技术的日益普遍,其在手机上的应用也得到了广泛关注.作为全球第一款Android正面按压指纹识别手机,魅族MX4 Pro所搭载的国产指纹识别系统可谓是赚足了眼球,这就是由汇顶科技提供 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- 蓝鲸DevOps深度解析系列(2):蓝盾流水线初体验
关注嘉为科技,获取运维新知 前面一篇文章<蓝鲸DevOps深度解析系列(1):蓝盾平台总览>,我们总览了蓝鲸DevOps平台的背景.应用场景.特点和能力: 接下来我们继续解析蓝盾平台的 ...
随机推荐
- Python中的UnboundLocalError是什么错误?如何解决?
在一个月黑风高的夜晚,我们满心欢喜地写出以下代码: money = 10000 # 当前的财产,单位为元 def add_money(value): money += value print('当 ...
- 小知识:OGG的TRANLOGOPTIONS MINEFROMACTIVEDG参数
最近客户有一个需求,OGG源端需要配置在ADG环境,按历史配置规范,开启抽取进程报错: 2020-08-26 18:02:27 ERROR OGG-00060 Extract requires a v ...
- 【OpenVINO™】在 MacOS 上使用 OpenVINO™ C# API 部署 Yolov5
在 MacOS 上使用 OpenVINO C# API 部署 Yolov5 项目介绍 YOLOv5 是革命性的 "单阶段"对象检测模型的第五次迭代,旨在实时提供高速.高精度的结果, ...
- MySQL的CTE(公用表表达式)
(一)概念 MySQL的CTE是在MySQL8.0版本开始支持的,公用表表达式是一个命名的临时结果集,仅在单个SQL语句(例如select.insert.delete和update)的执行范围内存在. ...
- JS 保姆级贴心,从零教你手写实现一个防抖debounce方法
壹 ❀ 引 防抖在前端开发中算一个基础但很实用的开发技巧,在对于一些高频操作例如监听输入框值变化触发更新之类,会有奇效.除了实际开发,在面试中我们也可能偶遇手写防抖节流的问题,鉴于不同公司考核要求不一 ...
- Set与WeakSet
Set与WeakSet Set对象允许存储任何类型的唯一值,无论是原始值或者是对象引用,Set对象中的值不会重复. WeakSet对象允许存储对象弱引用的唯一值,WeakSet对象中的值同样不会重复, ...
- 栈溢出-ret2libc地址泄露笔记
作为一名初学者,在碰到很多攻击思路的时候会感觉很妙,比如gadget的构造,这题的sh参数截断. 1.首先分析程序架构和保护措施. 2.使用IDA开始判断程序是否具备最简单的栈溢出执行条件: ret2 ...
- Celey异步发送邮件时报django.core.exceptions.ImproperlyConfigured的解决办法
原main.py入口文件 #Celery的入口 from celery import Celery #创建Celery实例 生产者 celery_app = Celery('meiduo') #加载配 ...
- 【webserver 前置知识 03】Linux网络编程入门其二,I/O多路复用
I/O多路复用 I/O多路复用使得程序能够同时监听多个文件描述符 LInux下实现I/O多路复用的系统调用主要由select.poll以及epoll(常问,要会自己写出来) 例子 阻塞等待 阻塞等待可 ...
- 【LeetCode二叉树#18】修剪二叉搜索树(涉及重构二叉树与递归回溯)
修剪二叉搜索树 力扣题目链接(opens new window) 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R.通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) .你 ...