C++程序运行时内存布局之----------无继承情况下的虚函数

虚函数是C++实现多态的关键,没有虚函数,C++只能是OB,不能完成OO。对于VC++中虚函数的实现机制,有很多文章,我看过的是陈浩写的,链接如下:

http://blog.****.net/haoel/article/details/1948051。总觉得还是有些不太清楚的,所以自己做了进一步的实验,记录下来以供参考。

本文介绍的是没有继承情况下,带有虚函数的类在内存中布局,以及其实例(对象)内存布局。

1.源码

#include <iostream> #include <stdio.h> using namespace std; class CVirtual { public: int x; public: virtual void VF() { cout<<this->x<<endl; cout<<"hello"<<endl; } public: CVirtual(int x) { this->x = x; } }; typedef void (CVirtual::*Fun)(); union { Fun f; unsigned int addr; }ut; int main(int argc, char** argv) { //打印出类实例的大小 cout<<"对象大小为 :"<<sizeof(CVirtual)<<endl; CVirtual *p = new CVirtual(999); CVirtual *p1 = new CVirtual(888); //打印对象地址 cout<<"新建对象的地址 :"<<p<<endl; //打印第一个成员变量地址 cout<<"对象中第一个成员变量的地址 :"<<&p->x<<endl; //打印虚函数的地址 ut.f = &(CVirtual::VF); cout<<"类中第一个虚函数的地址 :"<<std::hex<<std::showbase<<ut.addr<<endl; //虚表指针 unsigned int vptr = *((unsigned int*)p); cout<<"对象1中虚表指针(即虚表地址)为 :"<<std::hex<<std::showbase<<vptr<<endl; //虚表第一项值为 unsigned slot0 = *((unsigned int*)vptr); cout<<"虚表中第一项的值(第一个虚函数地址):"<<std::hex<<std::showbase<<slot0<<endl; //虚表指针 unsigned int vptr1 = *((unsigned int*)p1); cout<<"对象2中虚表指针(即虚表地址)为 :"<<std::hex<<std::showbase<<vptr1<<endl; //虚表第一项值为 unsigned slot01 = *((unsigned int*)vptr1); cout<<"虚表中第一项的值(第一个虚函数地址):"<<std::hex<<std::showbase<<slot01<<endl; //虚函数的原型 typedef void (__thiscall *MyFun)(void* pThis); //调用虚函数表中的第一个函数 MyFun f = (MyFun)slot0; f(p); //调用虚函数 MyFun f1 = (MyFun)ut.addr; f1(p); delete p; delete p1; cin>>argc; }

2.结果

C++程序运行时内存布局之----------无继承情况下的虚函数

3.内存布局图

C++程序运行时内存布局之----------无继承情况下的虚函数

4.结论

  • 虚函数表在编译期间已经确定,建立在data区,运行期只是把由构造函数把对象的vptr值设定为这个地址,同一个类的所有实例共享同一个虚函数表;
  • 虚函数表中的每一个项目不一定是虚函数的地址,很可能是一个代理函数,然后由代理函数再调用虚函数;