C++虚函数表指针在内存中的位置
多态就是多种形态,C++的多态分为静态多态与动态多态。
- 静态多态就是编译器根据函数实参的类型判断出要调用哪个函数。比如函数重载和函数模板。
- 动态多态依靠的是虚函数表和动态绑定机制,因为是在运行时根据对象的类型在虚函数表中寻找调用函数的地址来调用相应的函数,所以称为动态多态。
C++是通过虚函数表来实现运行时多态的。通常所有声明为virtual的虚函数地址都被存放于该表中。编译器会为每个存在虚函数的类对象插入一个vtpr(virtul function pointer),该vptr指向存放了虚函数地址的虚函数表vtbl,这样对象在调用虚函数的时候,第一步会先根据vptr找到vbtl,然后根据该虚函数在vbtl中的索引来进行调用,这样就实现了运行时多态功能。也就是说,虚函数表是在编译期间就已经生成了!
虚函数表指针可以看成类中的一个成员变量,也应该占用一定的字节数(一般是4个或者8个字节)。既然该指针是类的成员变量,那么它在类的对象内存中就会有一个位置,它有可能位于对象内存的开头处,也有可能位于对象内存的末尾处,具体在什么位置取决于编译器。现在写一个范例,范例的目的是验证一下虚函数表和虚函数表指针的存在。
看看如下类A的定义代码:
#include <iostream>class A
{
public:int i; //一个成员变量,4字节virtual void testfunc() { std::cout << "virtual function testfunc!" << std::endl; } //一个简单的虚函数
};int main()
{A a;int ilen = sizeof(a); //8:i占4字节,虚函数表指针占4字节(皆在x86平台下)
}
现在可以确定一件事情,a对象里有一个虚函数表指针(vptr),还有一个成员变量i,成员变量是跟着对象走的,也就是说是属于该对象的。可以以图 2.5 做参照。
在 main主函数中继续增加如下的代码,用以感知一下虚函数表指针的存在。注意观察代码中的注释:
char* pl = reinterpret_cast<char*>(&a); //0x006ff730类型转换这属于硬转,a是对象首地址
char* p2 = reinterpret_cast<char*>(&(a.i)); //0x006ff734
if(pl== p2) //说明a和a位置相同则成员变量在a对象内存的上面位置那么虚丽数表指针在下面位置
{cout << "虚函数表指针位于对象内存的末尾" << endl;
}
else
{cout << "虚函数表指针位于对象内存的开头" << endl; //本条件会成立
}
执行起来,看一看结果
虚函数表指针位于对象内存的开头
上面的代码比较简单。不难看到,经过测试,虚函数表指针位于对象内存的起始(上面开头)位置。在 Linux 操作系统下编译并运行程序,得到的结果也相同(虚函数表指针也位于对象内存的起始位置)。
通过上面这个范例,感知到了虚函数表指针的存在,后续就要通过这个虚函数表指针来尝试调用一下虚函数。
该文章会更新,欢迎大家批评指正。
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器