C++虚函数实现原理和虚函数表

C++规定了虚函数的行为,但将实现方法留给了编译器作者。通常,编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表(virtual function table, vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址。

例如,基类对象包含这样一个指针,该指针指向基类中所有虚函数的地址表。派生类对象将包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址;如果派生类没有重新定义虚函数,该vtbl将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。注意,无论类中包含的虚函数是1个还是10个,都只需要在对象中添加1个地址成员,只是表的大小不同而已。

                                                                                 -------------摘自《C++ Primer Plus》

出于方便定位虚函数表的考虑,编译器一般将虚函数表的地址存放在对象内存的开头。

参考上图,我们可以做以下实验,

#include <iostream>
using namespace std;

typedef void (*PFUNC)(void);

class Base
{
    private:
        int price;
    public:
        virtual void prnmsg()
        {
            cout << "Where are you?" << endl;
        }
};


class Derive : public Base
{
    public:
    void prnmsg()
    {
        cout << "I'm here!" << endl;
    }
    virtual void prnmsg01()
    {
        cout << "Look out!" << endl;
    }
};

int main()
{
    Derive der;
    der.prnmsg();

    long vtl = *(long*)&der;
    PFUNC fnc = (PFUNC)*((long*)vtl);
    fnc();
    fnc = (PFUNC)*((long*)vtl+1);
    fnc();

    return 0;
}

如上代码,为了将虚函数地址赋给函数指针,并使用函数指针执行该虚函数,

首先取对象的地址,&der,由于虚函数表地址是存放在对象内存开头且是一个地址,在64位机中,地址占有8个字节的长度即long类型大小。因此(long*)&der,将对象的首地址强转成(long*),再使用*运算符                  *(long*)&der,得到对象首地址存放的虚拟表地址数值,使用vtl保存该数值

long vtl = *(long*)&der;

vtl保存的8字节数值实际上是一个有效地址,指向虚函数表数组的首地址,因此再次使用*运算符便可以得到对象第一个虚函数的入口地址,                               

  PFUNC fnc = (PFUNC)*((long*)vtl);

同理,对象第二个虚函数的入口地址为

fnc = (PFUNC)*((long*)vtl+1);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值