虚函数表

研究虚函数之前,我们先看下虚函数特性

在某个类中,普通成员函数是不占用类结构大小空间的,如果在它前面加上virtual修饰,那么这个成员函数称为虚函数

并且该类大小就会多出来4个字节,无论有几个vitrual虚函数,始终会多出来4个字节,那么我们就来研究研究多出来这4个字节是什么东西 

我们先看看没用virtual虚函数情况下类结构的内存布局

// 虚函数.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

class plus{
public:
	int x;
	int y;
	plus() {
		x = 1;
		y = 2;
	};
};
int main()
{	
	plus p;
	plus *pi = &p;
	printf("%x",pi);
	//printf("%d",sizeof(p));
	getchar();
    return 0;
}

它的前4个字节放的是成员数据 int 1

后4个字节放的是成员数据 int 2

如下图所示:

2x.png

那么我们在看看加上virtual虚函数它的内存布局

// 虚函数.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

class plus{
public:
	int x;
	int y;
	plus() {
		x = 1;
		y = 2;
	};
	virtual void func() {}
	virtual void func_1(){}
};
int main()
{	
	plus p;
	plus *pi = &p;
	printf("%x",pi);
	//printf("%d",sizeof(p));
	getchar();
    return 0;
}

3x.png

可以看到 这里我加了两个两个虚函数,当我在一个对象加一个virtual的时候,它会在当前类对象首地址多出来4个字节

那么它的内存首地址4个字节变成了 00986b34 那么这个就是传说中的vptr指针 它里面是什么东西我们继续分析

好 那么我通过指针调用这个virtual 我们看反汇编

plus p;
plus *pi = &p;
pi->func();
004018F7 8B 45 E0             mov         eax,dword ptr [pi]  
004018FA 8B 10                mov         edx,dword ptr [eax]  
004018FC 8B F4                mov         esi,esp  
004018FE 8B 4D E0             mov         ecx,dword ptr [pi]  
00401901 8B 02                mov         eax,dword ptr [edx]  
00401903 FF D0                call        eax  
00401905 3B F4                cmp         esi,esp  
00401907 E8 20 F8 FF FF       call        __RTC_CheckEsp (040112Ch)  

eax.png

eax.pngeax[].pngeax[]-.pngcall.png

通过跟踪反汇编我们可以得出结论

当一个类成员函数包含virtual的时候,那么该类对象首地址4个字节存放了一个vptr指针,这个指针指向一个虚函数表,存放的是所有的函数地址

那么我们用函数指针验证该结论的正确性

#include <stdio.h>
class plus{
public:
	int x;
	int y;
	virtual void fun(){
		printf("虚函数fun()\n");
	};
	virtual void fun2(){
		printf("虚函数fun2()\n");
	};
	virtual void fun3() {
		printf("虚函数fun3()\n");
	};
	virtual void fun4() {
		printf("虚函数fun4()\n");
	}
};
void main(int argc, char const *argv[])
{
	plus p;
	typedef  void(*pfuns)(void);
	pfuns pfun;
	//*((int*)*((int*)&p)+0)


	for (int i = 0; i < 4; i++)
	{
		void(*pfun)(void) = (void(*)(void))*((int*)*((int*)&p) + i);
		//pfun = (pfuns)*((int*)*((int*)&p) + i);
		
		(int*)&p; //对象首地址
		*((int*)&p); //对象首地址前4字节 虚函数表的地址
		*((int*)*((int*)&p) + 0); //取出虚函数表第一个4字节存放的内容 虚函数真正的地址
		
		pfun();
	}	
	
	/* code */
	getchar() ;
	//return 0;
}

执行结果:

QQ20190213-015213@2x.png

本博客所有文章如无特别注明均为原创。作者:odaycaogen复制或转载请以超链接形式注明转自 123``blog
原文地址《虚函数表

相关推荐

发表评论

路人甲 表情
Ctrl+Enter快速提交

网友评论(0)