6.1虚函数和虚表

虚函数和虚表

虚函数
1.在类的定义中,前面有 virtual 关键字的成员函数称为虚函数;
2.virtual 关键字只用在类定义里的函数声明中,写函数体时不用。

class Base 
{
    virtual int Fun() ; // 虚函数
};

int Base::Fun() // virtual 字段不用在函数体时定义
{ }

什么是虚表呢?

在C++语言中,每个有虚函数的类或者虚继承的子类,编译器都会为它生成一个虚拟函数表(简称:虚表),表中的每一个元素都指向一个虚函数的地址。(注意:虚表是从属于类的)

这么说肯定没法理解,那么我们就来从底层一步一步的来刨析一下虚表,虚函数到底是什么东西。有了这些基础后我们再学习多态。

首先回忆以前的知识点:
成员函数并没有绑定在对象上面
在这里插入图片描述
所以我们这个位置大小是1,空class大小为1

新东西来了:

在这里插入图片描述
offsetof是求偏移量:简单来说就是我们的类A到了m_a偏移了多少个字节,当我们只有一个虚函数的时候很明显只偏移了四个字节。

那么这四个字节到底是什么呢?

我们再来看看两个虚函数的情况,偏移量到底是多少?

在这里插入图片描述

可以看到偏移量还是四个,但是我们有两个虚函数,那么这四个字节到底是什么东西,我们用的是vs2019,我们可以知道指针的大小是四个字节,但是这四个字节真的是指针吗?我们来刨析一下这四个字节到底是什么,我们可以通过vs看到底层模型:

在这里插入图片描述
从代码可以看到我们创建了一个A类的对象a

a中包括了_vfptr和m_a还有m_b,所以我们能可以断定出这个四个字节就是_vfptr,那究竟是不是指针呢?

我们可以看到这个位置:

在这里插入图片描述
这个_vfptr的类型是void**,__vfptr是一个指针的指针

在这里插入图片描述
指向的是这个东西,那么这个是什么呢?我们再添加一个虚函数试一试。

那么我们的代码就变成了这个:

在这里插入图片描述

我们再来看看底层:

在这里插入图片描述
增加一个虚函数, 只是简单地向该类对应的虚函数表中增加一项而已, 并不会影响到类对象的大小以及布局情况。

前面已经提到过: __vfptr只是一个指针, 她指向一个数组,一个函数指针数组!, 并且: 这个数组没有包含到类定义内部, 那么她们之间是怎样一个关系呢?

先上类的代码:

#include <iostream>
using namespace std;
struct Base1
{
public:
    int base1_1;
    int base1_2;

    virtual void base1_fun1() {}
    virtual void base1_fun2() {}
};
class Base2 : public Base1
{

};
int main()
{
	Base1 a1;
	Base1 a2;
}

我们来看看刚刚编译完成后的情况(注意刚刚编译完成后几个字):

在这里插入图片描述

我们可以会很明显的看到a1中的[0],[1]和a2[0],a2[1]中的值并不相同,别急,我们接着看:

在这里插入图片描述

图片左上角有一个黄色箭头标记,指向了19行,代表下一步即将运行19行,我们可以对比第一张图,我们的a1中[0],[1]的值已经发生改变,那我们程序接着运行:

在这里插入图片描述

看见没,a1中[1],[0]和a2中的[1],[0]指向的东西的值都相同。注意,我说的是值。存放这些值的地方我们就称为虚表!

注意了,前面我说了刚刚编译结束的时候为什么值不一样,你可以这样理解,刚开始我们都还没开始构造,所以系统随机分配。

在这里插入图片描述
这是一个特别重要的东西,我写完了再回来加上的:!

为什么我们的这个位置只有两个虚函数,但是我们却显示:
在这里插入图片描述

有三个!我最开始还真没注意到,也没仔细想,但是后面为了弄明白,我去访问了第三个的位置,发现他是一个NULL指针:
在这里插入图片描述
虚表的最后的位置是一个NULL指针!!!!!!!系统自动添加,所以虚表的大小比虚函数多个1!!!!!!!!!!!!!!!!

有了上面的这个铺垫,那么我们就可以抬出我们的简单结论:同一类使用一份虚表,还不够直观对吧,那么我们来画图理解一下。放出我们的对象模型:

在这里插入图片描述

注意了,我们a1中vfptr中的地址并不等于vfptr,只是他们的地址中存储的值是相同的,是指向虚表的!

那么问题就来了! 这个虚函数表保存在哪里呢? 其实, 我们无需过分追究她位于哪里, 重点是:

1.她是编译器在编译时期为我们创建好的, 只存在一份
2.定义类对象时, 编译器自动将类对象的__vfptr指向这个虚函数表

大家有没有留意这个__vfptr? 为什么它被定义成一个指向指针数组的指针, 而不是直接定义成一个指针数组呢?我为什么要提这样一个问题?

因为如果仅是一个指针的情况, 您就无法轻易地修改那个数组里面的内容, 因为她并不属于类对象的一部分.属于类对象的, 仅是一个指向虚函数表的一个指针__vfptr而已!

这一章节是为了 我们下一篇文章的多态做铺垫。

如果有问题可以私聊作者。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值