前面我們已經知道, 在沒有虛函數的時候, 對象的大小就是對應的成員變量的大小, 而成員函數不會占用對象的空間, 今天我們來討論一下, 當類中定義了虛函數的時候, 此時對象的大小以及對象模型
非繼承下的對象模型
class Base
{
public:virtual void func1(){cout << "Base::func1()" << endl;}virtual void func2(){cout << "Base::func2()" << endl;}
private:int a;
};
void Test1()
{Base b1;
}
此時打開編譯器, 觀察 b1 的對象模型如下圖所示
此時我們會發現對象b1 它除了有一個成員變量 a 之外, 它還有一個虛函數表, 這個虛函數表的數據結構是由一個指針數組來維護的, 這個指針數組中存放了指向虛函數的指針, 并且指向虛函數指針的結束標識在VS下是以一個0為結束標識, 也就是說 b1的對象模型如下圖所示
單繼承下的對象模型
class Base
{
public:virtual void func1(){cout << "Base::func1()" << endl;}virtual void func2(){cout << "Base::func2()" << endl;}
private:int a;
};class Derive :public Base
{
public:virtual void func1(){cout << "Derive: func1()" << endl;}virtual void func3(){cout << "Derive: func3()" << endl;}virtual void func4(){cout << "Derive: func4()" << endl;}
private:int b;
};typedef void(*VFUNC)();void PrintVTable(int* VTable)
{cout << "虛表地址: " << VTable << endl;int i = 0;for(; VTable[i] != 0; i++){printf("第 %d 個虛函數地址: 0x%x -> ", i, VTable[i]);VFUNC f = (VFUNC)VTable[i];f();}cout << endl;
}void Test1()
{Base b1;Derive d1;int*VTable1 = (int*)(*(int*)(&b1));int*VTable2 = (int*)(*(int*)(&d1));PrintVTable(VTable1);PrintVTable(VTable2);
}
為了方便起見, 我們將對應的虛表打印出來, 此時會發現如下現象
接下來探索一下對應的 b1 和 d1 的對象模型
多繼承對象模型
class Base1
{
public:virtual void func1(){cout << "Base1::func1()" << endl;}virtual void func2(){cout << "Base1::func2()" << endl;}
private:int b1;
};class Base2
{
public:virtual void func1(){cout << "Base2::func1()" << endl;}virtual void func2(){cout << "Base2::func2()" << endl;}
private:int b2;
};
class Derive :public Base1, public Base2
{
public:virtual void func1(){cout << "Derive: func1()" << endl;}virtual void func3(){cout << "Derive: func3()" << endl;}
private:int b;
};
此時的對象模型如下圖所示