c++虚函数表

虚函数

在名函数前加关键字“virtual”的函数为虚函数;

{            
public:            
    void Function_1()            
    {            
        printf("Function_1...\n");            
    }            
    virtual void Function_2()   //虚函数         
    {            
        printf("Function_2...\n");            
    }            
};

反汇编代码

在这里插入图片描述

总结

通过对象调用时,virtual函数与普通函数都是E8 Call

通过指针调用时,virtual函数是FF Call,也就是间接Call

IAT表使用的就是FF Call ;

​ 使用间接call的原因是因为虚函数的地址不靠谱,可能被改掉;

​ 比如,子类覆盖父类函数时;

纯虚函数

C++中的纯虚函数是没有函数体的虚函数,也称为“纯虚函数接口”。纯虚函数在基类中用于定义一个接口,其具体实现则由派生类完成。

纯虚函数使用纯虚函数语法 virtual returnType functionName() = 0; 声明,其中 =0 表示这个函数为纯虚函数,没有实现。

例如,下面是一个名为 Shape 的基类,其中包含一个纯虚函数 getArea()

class Shape {
  public:
    virtual double getArea() = 0;
    virtual ~Shape() {} // 确保析构函数为虚函数
};

派生类必须重写基类中的纯虚函数,否则它们也会成为抽象类。以下示例是一个名为 Rectangle 的派生类,它重写了 Shape 类中的 getArea() 函数:

class Rectangle : public Shape {
  public:
    double getArea() { return width * height; }
    void setWidth(double w) { width = w; }
    void setHeight(double h) { height = h; }
  private:
    double width;
    double height;
};

请注意,在纯虚函数声明后,必须提供括号中的 =0。纯虚函数不能包含函数体,否则编译器将抛出错误。

深入虚函数调用

#include "stdafx.h"
    class Base                     
    {      
    public:    
        int x;
        int y;
        Base(){
            x=1;
            y=2;
        }
        void fun(){
        }
        virtual fun2(){
        }
    };                      
                        
    int main(int argc, char* argv[])                    
    {                    
        Base b;
        Base* pb = &b;
        pb ->fun2();
 
        getchar();
        return 0;                
    }

上面结构的大小为 0x0C

普通函数放结构体里面时,不会影响结构体大小,结构大小应该是两个int的大小8个字节;

但在结构体中有一个或多个虚函数时,多了4个字节;

这四个字节其实是一个地址,指向虚函数表

在这里插入图片描述

ecx里存的是this指针,this指针指向结构的首地址,而多出的四字节就在结构的首地址处,所以this指针指向的地方里面存了四字节,这四字节指向了虚表的地址,

将42501c存入edx,然后FF call这个地址;

如果有多个虚函数反汇编:
在这里插入图片描述
在这里插入图片描述

可以看到,第一个虚函数call的是[edx],第二个虚函数call的是[edx+4];

总结

1、当类中有虚函数时,会多一个属性,4个字节

2、多出的属性是一个地址,指向一张表,里面存储了所有虚函数的地址

解析虚函数表

class Base                    
{                    
public:                    
    int x;                
    int y;                
    virtual void Function_1()                    
    {                    
        printf("Function_1...\n");                    
    }                    
    virtual void Function_2()                    
    {                    
        printf("Function_2...\n");                    
    }                    
    virtual void Function_3()                    
    {                    
        printf("Function_3...\n");                    
    }                    
};                    
                    
void TestMethod()                    
{                    
    //查看 Sub 的虚函数表                
    Base base;                    
                    
    //对象的前四个字节就是虚函数表                
    printf("base 的虚函数表地址为:%x\n",*(int*)&base);                
                    
    //通过函数指针调用函数,验证正确性                
    typedef void(*pFunction)(void);                    
                    
    pFunction pFn;                
                    
    for(int i=0;i<3;i++)                
    {                
        int temp = *((int*)(*(int*)&base)+i);            
        pFn = (pFunction)temp;            
        pFn();            
    }                
                    
}

在这里插入图片描述

练习

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

#include"c++test.h"

class 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");
	}
};
class 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");
	}
};

void test()
{
	Sub base;
	printf("虚函数表的地址:%X\n", *(int*)&base);
	typedef void(*pFunction)(void);
		pFunction pfn;
		for (int i = 0; i < 6; i++)
		{
			int Temp = *((int*)(*(int*)&base) + i);
			pfn = (pFunction)Temp;
			pfn();
		}
}
int main(int argc, char* argv[])
{
	test();
}

继承时先复制了父类的信息,所以虚函数表先存储父类的虚函数

在这里插入图片描述

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

#include"c++test.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");
    }
};

void test()
{
	Sub base;
	printf("虚函数表的地址:%X\n", *(int*)&base);
	typedef void(*pFunction)(void);
		pFunction pfn;
		for (int i = 0; i < 6; i++)
		{
			int Temp = *((int*)(*(int*)&base) + i);
			pfn = (pFunction)Temp;
			pfn();
		}
}
int main(int argc, char* argv[])
{
	test();
}

在这里插入图片描述
在这里插入图片描述

子类覆盖了父类的虚函数地址,所以前两个函数变成子类重写后的函数地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值