C++类对象中虚函数表指针、虚函数表、虚函数之间的关系以及在内存中的布局

目录

C++类的虚函数表和虚函数在内存中的位置

 虚函数表和虚函数在内存中的位置说明

结论


原文 C++类的虚函数表和虚函数在内存中的位置_JMW1407的博客-CSDN博客

C++类的虚函数表和虚函数在内存中的位置

关系:虚函数表指针(保存在堆或栈)->虚函数表(常量区 .rodata)->虚函数(代码段 .text)

虚函数表指针是虚函数表所在位置的地址。虚函数表指针属于对象实例。因而通过new 出来的对象的虚函数表指针位于堆,声名对象的虚函数表指针位于栈。

总结:

1.虚函数表指针位置取决于对象在哪。如果是new的对象,则存在堆上,如果是直接声明,则存在栈上。

2.虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区;

3.虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区

 
虚函数表和虚函数在内存中的位置说明

 先创建一个有虚函数的类A

class A
{
public:
    A(){};
    ~A(){};
    virtual void vfun(){cout<<"vfun called!"<<endl;}
};

既然要知道虚函数表的位置,那么自然就需要找到虚函数表的地址。我们知道,对于类A这种简单的类,其对象内存布局的最开始四个字节就是一个虚函数表指针(32位编译器),而这个指针变量的值自然就是虚函数表的地址了,因此,第一步就是获取这个虚函数表指针来找到虚函数表的地址

A *a = new A();

long vbaddr=*(int *)a;   //虚函数表地址

由于只有一个虚函数,所以虚函数表的前4个字节肯定就是vfun的函数地址,因此根据虚函数表的地址还可以得到虚函数vfun的地址:

long vfaddr= *(int *)vbaddr;  //虚函数A::vfun的地址

然后还可以根据vfun的地址来调用这个函数:

((void(*)(void))vfaddr)();  //根据得到的地址来调用虚函数 

如果通过vfaddr来调用函数是成功的,那么就说明前面虚函数表的地址都是正确的。

得出以下程序:

#include <iostream>
 
using namespace std;
 
class A
{
public:
    A(){};
    ~A(){};
    virtual void vfun(){cout<<"vfun called!"<<endl;}
};
 
int main()
{
    A *a = new A();
    long vbaddr=*(int *)a;   //虚函数表地址
    long vfaddr= *(int *)vbaddr;   //虚函数vfun地址
    cout<<"addr of vb : "<<vbaddr<<endl;
    cout<<"addr of vfun : "<<vfaddr<<endl;
    
    ((void(*)(void))vfaddr)();   //根据虚函数地址调用虚函数
 
    delete a;
    return 0;
}

用g++进行编译生成可执行文件,然后运行:

从运行结果可以看到,虚函数表的地址是0x400be0(4197344),虚函数vfun的地址为0x400aea(4197098),并且根据虚函数vfun的地址成功调用了虚函数,打印了“vfun called”,这说明获取的0x400be0确实是虚函数表的地址。

接下来就看看0x400be0这个地址在可执行文件内存中的哪个段。

用objdump -s 可以解析ELF格式的可执行文件中的分段信息:

每个分段的内容用Contents of section .xxx来分隔,xxx表示下面的内容属于哪个段。在这些段的内容中,每一行的第一个16进制数表示的是相应的段中的一个地址,以400238 2f6c6962 3634…这一行为例,首地址为0x400238,那么从0x400238到下一行首地址0x400248之间的16个字节中存放的数据就是0x2f 0x6c 0x69 0x62…

回到虚函数表的地址上来,前面说了,虚函数表的地址为0x400be0,现在来看看这个地址是属于哪个段:

可以看到,0x400be0这个地址,刚好就在.rodata这个段中,这个段就是C++中的常量区,并且还可以发现,从这个地址开始取4个字节“ea0a4000”,由于是小端模式,因此取出来的4字节数为0x400aea,是不是很眼熟呢?没错,这个地址就是前面求得的虚函数vfun的地址。

同理,根据虚函数vfun的0x400aea地址,还可以找到虚函数vfun的位置:

 可以看到,虚函数vfun位于.text代码段,也就是C++中的代码区。

结论

综上所述: 虚函数表指针区别于创建的对象是new还是直接声明而放于堆或栈上。C++中虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区。

参考
1、https://blog.twofei.com/496/
2、https://blog.csdn.net/Lily_zhangrongli/article/details/106650195
3、https://blog.csdn.net/qq_28114615/article/details/98041319

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子木呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值