【c++】虚函数(二)——虚函数表的验证

 在上篇博文中,类中的首地址多出来四个字节

以下将验证多出来的的四个字节究竟是不是虚函数表

 

————————————下面是正文——————————————

       在验证之前,首先来补充一些知识:函数指针

       根据我以前写的一篇《【c语言】带你真正走进指针的世界——函数指针》中可以得知:函数名就是函数的指针,代表了函数的真实地址。以下为函数指针的简单运用:

#include <iostream>

void test()
{
	std::cout<<"THE TEST IS ONE!"<<std::endl;
}

void main()
{
	void (*p)();
	p = test;
	p();
}

 编译出来的结果可以清晰的看到使用函数指针可以调用所指的函数:

                                                             

       而在本次的博文中,将引用函数指针来证实多出来的四个字节是否为虚函数表。首先,我们假设如果多出来的四个字节真的是虚函数表的话,那么这四个字节就是一个地址,而这个地址里所储存的内容就是第一个虚函数的地址。

                                                            

                                                图上为多出来的四个字节(假设为虚函数表的地址)

                                                             

                                       在地址栏输入 432028 ,可以看到地址432028 中存有数据 401064 

       了解以上知识后,我们首先要接触到的第一个问题是如何将地址 432028 中的地址 401064 取出来呢?在提取第一个虚函数的地址 401064 之前,我们需要将虚函数表的地址 43202c 先提取出来,顺着虚函数表的地址去找虚函数的地址。首先,我们可以尝试输出类名,观察输出的结果是什么东西:

printf("%x\n",b);

编译的结果可以看出,是多出来的四个字节:

       到了这里,就有了一个疑问:那我可不可以直接用这个赋值给函数指针,然后进行调用呢?答案是不行的,这个是虚函数表的地址,并不是虚函数的地址,我们需要的是虚函数的地址赋值给函数指针,而且,使用类名输出的结果虽然只有一个,然而实际上代表的是整个类,只是类名代表了类的首地址,所以直接拿出来用也就相当于将整个类赋值给函数指针,并不是我们需要的结果。

       所以,我们需要的是取出类中的前四个字节,所以,我们需要一步一步地将类中所储存的 前四个字节 提取出来,首先,我们需要对类名进行赋值(加星)处理:

printf("%x\n",&b);

编译的结果可以看出,输出为类的首地址:

然后,我们需要的是类中所储存的前四个字节,所以,我们进行一个强制转化类型,使得输出只取四个字节:

printf("%x\n",(int*)&b);

编译的结果可以看到,虽然和上一个结果没有什么区别,实际上类型已经变成了一个 int* 类型的指针,指针大小为 四个字节

最后,如果我们要将一个指针所存储的内容取出来的话需要进行什么操作呢?是的,进行降星就可以了:

printf("%x\n",*(int*)&b);

       如此这般,最后得到的结果虽然和第一个图片的结果相一致,但实际上差距却天差地别,还是不懂的同学可以多看几次我上面的解释~

       有了上面的基础,我们也就很容易地可以得到地址 432020 里面所储存的第一个地址(也就是第一个虚函数)了,也就是重复上面的步骤而已:

printf("%x\n",*(int*)*(int*)&b);

不明白的可以看下面的解释图:

后面,我们就可以直接使用指针函数,将上面  *(int*)*(int*)&b 赋值给函数指针,然后通过函数指针进行调用:

#include <iostream>

class base
{
public:
	base()
	{
		x = 1;
		y = 2;
	}

	int x,y;

	virtual function1()
	{
		std::cout<<"The test is one!"<<std::endl;
	}
};

void main()
{ 
  base b;
  int (*p)();
  p = (int(*)())(*(int*)(*(int*)&b));
  p();
}

      编译出来的结果可以看到,打印出来虚函数中的字符:

由此可以证明,类中多出来的四个字节,确实就是虚函数表的地址。

如果想要测试两个或者以上的虚函数,可以将上述代码中的 (int(*)())(*(int*)(*(int*)&b)+N) 来实现函数的调用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章