时间:2022-04-24 10:16:52 | 栏目:C代码 | 点击:次
本文简单探究虚表实现。
如下代码中有一个基类CPerson
和子类CStudent:public
.并有两个虚函数run
和cry
。
#include<iostream> using namespace std; class CPerson { public: virtual void run() { cout << "Person is runing. \r\n"; } virtual void cry() { cout << "Person is cry. \r\n"; } }; class CStudent:public CPerson { public: void run() { cout << "Student is runing. \r\n"; } void cry() { cout << "Student is cry. \r\n"; } }; int main() { CPerson * pStudent = new CStudent(); pStudent->run(); getchar(); return 0; }
VS
中cl
提供了对应命令行查看类的内存布局。
语法cl /d1 reportSingleClassLayoutXXXX YYYY
。其中XXX为类名,YYYY
为所在文件全路径。
如本例:
cl /d1 reportSingleClassLayoutCStudent \\Mac\Main.cpp
输出如下:
我们再看下CPerson
的输出:
上面的图可能很抽象,大致的意思一个CStudent
的内存区域中的第一个内存区域存放一个4字节指针
,指针指向一个另一个内存区域,这个内存区域我们称为虚表。虚表内存结构可以理解为一个数组,数组中的每个元素是一个函数指针。指向函数地址。
示例图:
空说无凭,我们利用VS内存查看器和汇编器证实猜测。
上图简单来说就是:
(1) 构造了一个CStudent对象,地址为0x00d88d28
(2) 进入对象的地址,对象内存的第一个4字节区域为0x00f89b78
,这个存储的数值为虚表指针位置。
(3) 进入虚表内存地址,发现存储了两个指针 0xf8105f
,0xf81406
.
我们再次跳转到0xf8105f
内存地址,此时我们查看汇编级别代码。发现是一个jmp指令跳转到实际CStudent::run函数.
同理0xf81406
如下图所示:
其他扩展知识:
虚表在编译时已经构造完成,在构造对象时,会根据对象的类型在初始化时赋值虚表地址给对象的vfptr
。我们把这种行为称为动态绑定(Dynamic Binding)