C++对象内存布局--④VS编译器--单个虚拟继承
在VS2005编译器下,证明单个虚拟继承的内存布局:无论有无虚函数,必然含有虚基类表指针。虚基类表中的内容为本类实例的偏移和基类实例的相对偏移值。
如果有虚函数,那么基类的虚函数表跟派生类的虚函数表是分开的。
在内存布局上,地址从低到高,顺序如下:派生类的虚函数表指针,虚基类表指针,派生类的成员变量,基类的虚函数表指针,基类的成员变量。
也就是说派生类在上,基类在下。这个跟普通的继承相反。
特别说明,GNU的GCC编译器在处理虚拟继承上跟VS有不同的地方。它的内存布局是:派生类的虚函数表跟虚基类表合并,另外分析。
另外,发现如果派生类实现了基类的虚函数,那么派生类对象,派生类和基类的实例之间会多出一个值为0的间隔。
//VS编译器--单个虚拟继承.cpp
//2010.8.18
#include <iostream>
using namespace std;
//
class Base
{public:Base(int a = 10):a(a){cout << "Base::Base()" << endl;}virtual void show1(){cout << "Base::show1()" << endl;}private:int a;
};
//
class Derived : virtual public Base
{public:Derived(int b = 100):b(b){cout << "Derived::derived()" << endl;}virtual void show2(){cout << "Derived::show2()" << endl;}private:int b;
};
//
int main()
{Derived obj;int** p = (int**)&obj;typedef void (__thiscall *fun)(void*pThis);//非常重要cout << "虚拟继承了基类的派生类的对象内存布局:" <<endl;for (int i = 0; i != sizeof(obj)/4; ++i){cout << p[i] << endl;}cout << endl << "第一虚函数表第一项,虚函数Derived::show2()地址:" << (int*)p[0][0] << endl;((fun)(p[0][0]))(p);cout << "第二虚函数表第一项,虚函数Base::show1()地址 :" << (int*)p[3][0] << endl;((fun)(p[3][0]))(p+3);cout << endl << "虚基类表第一项,本类对象地址 - 虚基类表指针地址 = " << (int*)p[1][0] << endl;cout << "虚基类表第二项,基类对象地址 - 虚基类表指针地址 = " << (int*)p[1][1] << endl;system("pause");return 0;
}
/*
Base::Base()
Derived::derived()
虚拟继承了基类的派生类的对象内存布局:
0041C2F8
0041C2FC
00000064
0041C2F0
0000000A第一虚函数表第一项,虚函数Derived::show2()地址:00401280
Derived::show2()
第二虚函数表第一项,虚函数Base::show1()地址 :00401250
Base::show1()虚基类表第一项,本类对象地址 - 虚基类表指针地址 = FFFFFFFC
虚基类表第二项,基类对象地址 - 虚基类表指针地址 = 00000008
请按任意键继续. . .
*/