C++ 虚函数

本文深入探讨了C++中的虚函数,包括虚函数的实现机制,如虚函数地址表(虚表)和虚表指针。通过实例分析了虚函数的内存布局、调用过程以及在多态中的应用。此外,文章还讨论了构造函数、析构函数与虚表的关系,抽象类的虚函数处理,以及纯虚函数在虚表中的特殊表示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于虚函数而言,这篇博客主要侧重于理解虚函数的实现机制,还有就是如何实现的多态?

在C++中,使用virtual关键字声明的函数为虚函数。当类中有虚函数时,编译器会将该类中所有虚函数的首地址保存在一张表中,这张表也称为虚函数地址表(虚表),在我们代码层面可以理解为函数指针数组。同时编译器还会在类中添加一个隐藏数据成员,该成员称为虚表指针,该指针也就是用于保存前面所说的虚表首地址。

好了,下面就先来看一个简单的虚函数类

class CVirtual
{
public:
    virtual int GetNumber() //虚函数一
    {
        return m_nNum;
    }
    virtual void SetNumber(int num) //虚函数二
    {
        m_nNum = num;
    }
private:
    int m_nNum;
};

上面我们说了虚表指针的概念,对于虚表指针而言,其肯定会在对象的首地址处,由于是指针,所以其大小占用四个字节,那么可以说明上面的这个对象大小应该总共应该是8字节(虚表指针+成员字段m_nNum)

int main(int argc)
{
    CVirtual obj;
    int objSize = sizeof obj;
    return 0;
}

下面,我们可以通过调试来看一下其对象内存分布情况

对于上面其内存布局不太清楚的,可以再看一下下面这内存分配图

OK,上面主要说的是编译器对于虚函数而做的额外工作,当然这些工作是不可少的,下面我们来看看虚函数的调用,就可知道这些虚表和虚表指针的用途了。

int main(int argc)
{
    CVirtual obj;
    CVirtual *pObj = &obj;
    pObj->SetNumber(10); //虚函数调用
    return 0;
}

对于虚函数的实现调用,这里我们需要来简单的分析一下其汇编代码

    pObj->SetNumber(10);
002817EE 6A 0A                push        0Ah  //压入参数
002817F0 8B 45 E8             mov         eax,dword ptr [pObj]  //定位到虚表指针 vfptr
002817F3 8B 10                mov         edx,dword ptr [eax]   //获取虚表
002817F5 8B 4D E8             mov         ecx,dword ptr [pObj]  //thiscall 需要传递对象首地址,vfptr肯定在对象首地址的前四个字节,所以其也是虚表指针的位置
002817F8 8B 42 04             mov         eax,dword ptr [edx+4] //虚表的第二项 
002817FB FF D0                call        eax  //调用成员函数

结合上面的图片观察,可以发现SetNumber函数正是在函数指针数组的第二项。那么对于虚函数的调用,其本质就是查虚表然后获取其函数地址并调用。

对象
    首四字节-vfptr
        fun1
        fun2
1.获取对象的首四个字节(虚表指针)
2.通过该指针定位到虚表
3.获取虚表内的函数地址
4.调用-(比较明显的特性这里是间接调用)

OK,通过上面的分析,应该对虚函数有了一个宏观上的理解了,下面我们来考虑一些问题,用于加深我们的印象

首先,虚函数必须作为成员函数使用吗?

这里答案显然是肯定的,因为对于非成员函数而言,其没有this指针,也就是无法获取虚表指针,而无法定位到虚表,自然也就无法获取虚函数的地址了。

下个问题,对于上面的例子,可以发现其使用的是指针调用?为什么不使用对象调用

这种查虚表调用的情况只有在指针或者引用的情况下才会出现,因为使用对象调用,其目的很明显,无需进行查表调用,直接调用自身的成员函数即可。

虚表是何时产生的?

虚表信息会在编译后链接到对应的可执行文件中(也就是在你按下编译的那一瞬间),好比编译器使用了一个全局变量用于保存这些虚函

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值