对C++虚函数的理解

以前只知道虚函数是由一个叫作“虚函数表”的东西实现,但是一直不知道具体是怎么实现,今天看到csdn学院上c++虚函数的原理时,有几个问题,还是不怎么清楚,只好做一些测试,具体让我产生疑问的截图如下:

 

从上面的截图里,主要有2个问题

1.按上图所说,编译器会往类中插入“指向虚函数表的指针(__vfptr)”,那么如果这个类所生成几个不同的对象,这几个对象的__vfptr都相同吗?

2.子类和父类是共用一个虚函数表吗?

 

关于这两个问题,我写了一个程序来验证

#include "stdio.h"
class Animal
{
public:
    virtual void  eat()
    {
        printf("animal  eat\n");
    }
    virtual void crazy()
    {
        printf("animal  crazy\n");
    }
};

class Cat :public Animal
{
};
class WhiteCat :public Cat
{
public:
    void crazy()
    {
        printf("WhiteCat crazy\n");
    }
};
void main()
{
    Animal animal1;
    Animal animal2;

    printf("animal1 __vfptr:%X\n",animal1);    
    printf("animal2 __vfptr:%X\n", animal2);

    Cat   cat1;
    Cat   cat2;
    printf("cat1 __vfptr:%X\n",cat1 );
    printf("cat2 __vfptr:%X\n",cat2);
    
    WhiteCat whiteCat1;
    WhiteCat whiteCat2;
    printf("whiteCat1 __vfptr:%X\n", whiteCat1);
    printf("whiteCat1 __vfptr:%X\n", whiteCat2);

    getchar();
}

 

在getchar()函数下一个断点启动调试,此时程序运行如下

1可以看见animal1和animal2的__vfptr是一样的,同样cat1和cat2,whitecat1和whitecat2也是如此,但是cat和animal的__vfptr却并不相同

分析:(1)animal1和animal2都是Animal类生成的实例,他们指向了同一个虚函数表,同理cat1和cat2,whitecat1和whitecat2也是如此

           (2)尽管Cat是从Animal派生,并且Cat也没有改写基类的虚函数,但是该类生成的实例所对应的__vfptr却与基类不一样,这也就意味着基类和派生类并没有共用一个虚函数表

 

 

下面继续分析,虚函数表是怎么实现的,

(1)在vs里,点击右键---转到反汇编

 

(2)在汇编界面地址栏里输入0xDB7B34(animal1和animal2的__vfptr指向的地址),并回车

(3)我们可以看到animal1和animal2的__vfptr指向的地址存储了7C 11 DB 00 4F 11 DB 00 00 00 00 00 ...等字节,我们知道__vfptr是指向虚函数表的,而虚函数表可以看成是函数指针数组(存储函数指针的数组),我们先取前4个字节看看到底是不是指向函数指针,前4个字节是7C 11 DB 00,因为x86结构是属于“小端”架构(低字节在低位,高字节在高位),所以这4个字节对应的函数指针是00DB117C,此时我们在汇编界面的地址栏输入0xDB117C,并回车,如下图所示

(4)很明显0xDB117C存储的是一段汇编代码 jmp  Animal::eat,了解汇编代码的人都知道,该指令是无条件跳转指令,直接跳转到Animal::eat的汇编代码段

用同样的方法测试下4个字节4F 11 DB 00

接下来的 00 00 00 00 这4个字节,是虚函数表的结尾标志。

大家也可以用同样的方法查看派生类的虚函数表。。。

至此,我们终于得见虚函数表的真实面目啦,也许有人会问这个虚函数表好像跟想象的不一样,虚函数表不是存储虚函数地址的吗,这里怎么会是jmp指令,虽然最终可以通过这个jmp指令转到虚函数地址,但是他终究不是虚函数地址呀,难道书上面说错了吗?(答:书上没有说错,因为这是debug调试版本,在debug调试版本里是会出现jmp指令的,但release版本就不是jmp指令了,具体debug版本为什么要有jmp,为什么要多此一举,就下次再说吧)

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值