vtable

编译器会为每一个包含虚函数的类(或通过继承得到的子类)生成一个表,其中包含指向类中每一个虚函数的指针,这样的表就叫做虚表(vtable)

__vfptr

每个包含虚函数的类对象都获得__vfptr指针,并且是对象的第一个数据成员

编译器必须要保证虚函数表的指针存在于对象实例中最前面的位置

在计算对象的总大小时,也必须考虑到虚表指针。比如new,传递给new的大小值不仅包括类(以及任何超类)中的所有显式声明的字段占用的空间,而且包括虚表指针所需的任何空间 
示例代码:

class Base
{
public:virtual void vf1() = 0;virtual void vf2(){ cout<<"Base::vf2"<<endl; }virtual void vf3(){ cout<<"Base::vf3"<<endl; }virtual void vf4(){ cout<<"Base::vf4"<<endl; }
private:int x,y;
};class Sub: public Base
{
public:virtual void vf1(){cout<<"Sub::vf1"<<endl; }virtual void vf3(){cout<<"Sub::vf3"<<endl; }virtual void vf5(){cout<<"Sub::vf5"<<endl; }
private:int z;
};void call_vf(Base* b)
{b->vf3();
}int _tmain(int argc, _TCHAR* argv[])
{Base *b = new Sub;call_vf(b);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

IDA分析如下: 

值得注意的是,虚表索引操作非常类似于结构体引用操作。实际上,它们之间并无区别 
。因此,我们可以定义一个结构体来表示一个类的虚表的布局,然后利用这个已定义的结构体来提高反汇编代码清单的可读性 
 
这个结构体允许将虚表引用操作重新格式化成以下形式: 
 
注意:

C++虚函数绝不会被直接引用,也绝不应成为调用交叉引用的目标。所有C++虚函数应由至少一个虚表条目引用,并且始终是至少一个偏移量交叉引用的目标。

我们随便找到 vf1函数定义,然后点击它的交叉引用: 
 
从而跳转到虚表,其显示如下:

.rdata:004031B8 const Base::`vftable' dd offset __purecall ; DATA XREF: Base::Base(void)+A↑o
.rdata:004031BC                 dd offset Base::vf2(void)
.rdata:004031C0                 dd offset Base::vf3(void)
.rdata:004031C4                 dd offset Base::vf4(void)
.rdata:004031C8                 dd offset const Sub::`RTTI Complete Object Locator'
.rdata:004031CC const Sub::`vftable' dd offset Sub::vf1(void)
.rdata:004031CC                                         ; DATA XREF: Sub::Sub(void)+12↑o
.rdata:004031D0                 dd offset Base::vf2(void)
.rdata:004031D4                 dd offset Sub::vf3(void)
.rdata:004031D8                 dd offset Base::vf4(void)
.rdata:004031DC                 dd offset Sub::vf5(void)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看到,类构造函数Sub:: Sub(void)使用了虚表的地址Sub::Sub(void)+12↑o, 那是因为new sub 调用了sub的隐式构造函数 

windbg观察: 


注意,Base的虚表第一个是002816e4 Test!purecall,这时因为在Base中,没有针对纯虚函数vf1的实现,所以在Base的虚表中并没有存储vf1的地址,这时,编译器会插入一个错误处理函数的地址,通常,该函数名为purecall,一旦它被调用,程序就会被终止

附 
可以观察到:

1.new指针不为空,才会调用构造函数,这是由Visual C++的内部机制来保证的 
2.函数的虚表位置一般都可以在构造函数中找到(很明显,this指针指向的地址就是虚表的位置)

12.IDA-虚函数和虚表相关推荐

  1. C++——多态|虚函数|重写|虚表

    文章目录 1. 多态的概念 1.1 概念 2. 多态的定义及实现 2.1多态的构成条件 2.2 虚函数 2.3虚函数的重写 虚函数重写的三个例外: 2.4 普通调用和多态调用: 2.5 C++11 o ...

  2. C++_vptr与vtbl,虚函数与虚表

    C++_vptr与vtbl,虚函数与虚表 一:虚函数表指针(vptr)创建时机 vptr跟着对象走,所以对象什么时候创建出来,vptr就什么时候创建出来,也就是运行的时候. 当程序在编译期间,编译器会 ...

  3. C++ 虚函数和虚表

    几篇写的不错的文章,本文是整合了这几篇文章,感谢这些大佬 https://www.jianshu.com/p/00dc0d939119 https://www.cnblogs.com/hushpa/p ...

  4. 虚函数、虚表的生成,虚表的修改

    接上文. 虚函数.虚表在没有实例的情况下是无法从语法层面进行访问的. 那么其到底有没有生成呢? #include<iostream> using namespace std;class A ...

  5. 虚函数,虚表深度剖析

    面向对象,从单一的类开始说起. class A { private:int m_a;int m_b; }; 这个类中有两个成员变量,都是int类型,所以这个类在内存中占用多大的内存空间呢? sizeo ...

  6. 虚函数和虚表指针的例子

    [2021年9月2日 add] C++中, 一旦某个成员函数在基类中声明为虚函数,则它在所有的子类中都会成为虚函数. 换言之,如果基类中已经声明了某个函数为虚函数,则无需在子类中使用关键字virtua ...

  7. C++之菱形继承与虚继承(含虚函数)

    面向对象的三大特征:封装,多态,继承 前面我们已经讲了继承的一些知识点,在这基础上,我们讲的时候再涉猎一些多态的只是. 下面我们先接着上次讲有虚函数的菱形虚继承 首先什么是虚函数.? 虚函数:在类里面 ...

  8. C++中虚函数与多态实现

    多态,什么是多态?在计算机语言中,多态就是指一个接口或者方法,有多种展现形态.在C++中,通过父类指针调用子类方法,可以让父类指针有多种形态. C++中实现多态的方式有:虚函数,重载,模板,绑定等.此 ...

  9. C++类内存分布——深度理解继承与虚函数

    1.前言与准备 工欲善其事,必先利其器,我们先用好Visual Studio工具,像下面这样一步一步来:       先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 report ...

  10. java 析构函数_C++虚函数

    码字不易,欢迎给个赞! C++虚函数是多态性实现的重要方式,当某个虚函数通过指针或者引用调用时,编译器产生的代码直到运行时才能确定到底调用哪个版本的函数.被调用的函数是与绑定到指针或者引用上的对象的动 ...

最新文章

  1. java代码程序_Java程序代码
  2. 比较不错的一个ios找茬游戏源码
  3. SAP ByD 期末结账步骤简化(不完整)方法
  4. Leetcode题库 119.杨辉三角(单数组迭代 C实现)
  5. 数据挖掘—朴素贝叶斯分类算法(Java实现)
  6. 突破性能极限——阿里云神龙最新ASPLOS论文解读
  7. Windows XP 下安装Perl cpan模块
  8. 华为VLAN聚合原理与实验
  9. php生成一个500错误_Hyperf 发布 v2.0.1 版本 | 企业级的 PHP 微服务云原生协程框架...
  10. 操作系统课程设计(作业调度、内存管理、进程调度、进程阻塞等)
  11. 3D游戏中的数学基础
  12. Xmind8 Pro 最新版 破解教程(序列号|破解文件)
  13. matlab求零点和极点,传递函数的零点和极点.PPT
  14. python 线性插值处理_python线性插值解析
  15. 理性分析:那些吃蝙蝠的人,根本动机是什么?
  16. 如何批量删除pdf中的批注
  17. 股权融资的A、B、C、D轮
  18. vue3.0的多种写法,你喜欢哪种呢?
  19. [hdu 2826] The troubles of lmy [简单计算几何 - 相似]
  20. 软件设计师下午真题及参考答案

热门文章

  1. 缓存-SpringCache-自定义缓存配置
  2. Request_继承体系
  3. HTTP_请求消息_请求行
  4. SpringBoot profile配置
  5. 设置某个元素的标签内容、设置元素的样式、层次选择器、总结选择器
  6. php5.6.16,OSX 10.11 中重新编译PHP5.6.16问题
  7. 不同服务器怎么响应ajax,如何从服务器获得响应而无需刷新和使用JQuery/AJAX?
  8. oraclek导出表_全兼容Oracle?扒一扒浪潮K-DB是咋做的?
  9. 利用python进行数据分析第二版pdf百度云_参考《利用Python进行数据分析(第二版)》高清中文PDF+高清英文PDF+源代码...
  10. 牛客网Wannafly模拟赛