(虚函数表)

1.虚函数:间接调用

1.使用base调用函数

首先我们观察通过base对象来调用正常函数和带有virtual的函数的反汇编

此时我们可以看到ebp-4即this指针的地址作为参数传参。带virtual函数和普通函数都是使用E8 call

2.使用指针调用参数

可以看到带virtual函数使用FF call调用,查看反汇编call的是dword ptr [edx],表示此时访问的是this指针中第一个dword字节中的内容。

可以得出结论:

使用指针调用虚函数时,是使用间接调用的方式(FF call),call的是this指针中第一个dword字节的内容

2.深入虚函数调用

1.当类中有虚函数时,观察大小的变化

可以观察到有虚函数的类的大小要比之前的大4字节,我们再测试多个虚函数时,同样只是大于四字节。此时我们可以猜测这四字节可能是一个地址,此地址存储了一个虚函数表。

3.打印虚函数表

我们已经猜测多出的四个字节是虚函数的地址,因此我们需要通过调用该地址来调用函数来印证是否为真。

通过访问this指向第一个dword字节作为地址的(所谓虚函数表)的第一个内容(虚函数地址)作为函数指针可以调用虚函数。因此我们可以确定

对象地址处的第一个4字节的内容是一个虚函数表的地址,此地址指向虚函数表,虚函数表以4字节存储着连续的虚函数的地址。

4.作业

1、单继承无函数覆盖(打印Sub对象的虚函数表)

2、单继承有函数覆盖(打印Sub对象的虚函数表)

1.
#include<stdio.h>
struct Base				
{				
public:				
    virtual void Function_1()				
    {				
        printf("Base:Function_1...\n");				
    }				
    virtual void Function_2()				
    {				
        printf("Base:Function_2...\n");				
    }				
    virtual void Function_3()				
    {				
        printf("Base:Function_3...\n");				
    }				
};				
struct Sub:Base				
{				
public:				
    virtual void Function_4()				
    {				
        printf("Sub:Function_4...\n");				
    }				
    virtual void Function_5()				
    {				
        printf("Sub:Function_5...\n");				
    }				
    virtual void Function_6()				
    {				
        printf("Sub:Function_6...\n");				
    }				
};				

int main()
{	
	Sub sub;
	typedef void (*pFunc)(void);
	pFunc fp=NULL;
	for(int i=0;i<6;i++)
	{
		fp=(pFunc)*(((int*)*(int*)&sub)+i);
		fp();
	}
	return 0;
}

我们可以看到当虚函数没有重写时,首先调用的是父类的虚函数,然后再调用子类的。

2.

#include<stdio.h>

struct Base					
{					
public:					
    virtual void Function_1()					
    {					
        printf("Base:Function_1...\n");					
    }					
    virtual void Function_2()					
    {					
        printf("Base:Function_2...\n");					
    }					
    virtual void Function_3()					
    {					
        printf("Base:Function_3...\n");					
    }					
};					
struct Sub:Base					
{					
public:					
    virtual void Function_1()					
    {					
        printf("Sub:Function_1...\n");					
    }					
    virtual void Function_2()					
    {					
        printf("Sub:Function_2...\n");					
    }					
    virtual void Function_6()					
    {					
        printf("Sub:Function_6...\n");					
    }					
};					


int main()
{	
	Sub sub;
	typedef void (*pFunc)(void);
	pFunc fp=NULL;
	for(int i=0;i<6;i++)
	{
		fp=(pFunc)*(((int*)*(int*)&sub)+i);
		fp();
	}
	return 0;
}

此时运行的结果为:如果有重写情况时,先调用的是子类的虚函数,再调用父类的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值