有兩個概念能夠解釋C++對象模型:html
語言中直接支持面向對象程序設計的部分。
對於各類支持的底層實現機制。前端
數據成員分爲靜態和非靜態,成員函數有靜態非靜態以及虛函數ios
class data members:static和nonstatic函數
class data functions:static、nonstatic和virtual佈局
好比:spa
class Base { public: Base(int i) :baseI(i){}; int getI(){ return baseI; } static void countI(){}; virtual void print(void){ cout << "Base::print()"; } virtual ~Base(){} private: int baseI; static int baseS; };
簡單對象模型:這個模型很是地簡單粗暴。在該模型下,對象由一系列的指針組成,每個指針都指向一個數據成員或成員函數,也便是說,每一個數據成員和成員函數在類中所佔的大小是相同的,都爲一個指針的大小。這樣有個好處——很容易算出對象的大小,不過賠上的是空間和執行期效率。因此這種對象模型並無被用於實際產品上。設計
表格驅動對象模型:把類中的數據分紅了兩個部分:數據部分與函數部分,並使用兩張表格,一張存放數據自己,一張存放函數的地址(也即函數比成員多一次尋址),而類對象僅僅含有兩個指針,分別指向上面這兩個表。這樣看來,對象的大小是固定爲兩個指針大小。這個模型也沒有用於實際應用於真正的C++編譯器上。指針
C++對象模型:正在使用的調試
在此模型下,nonstatic 數據成員被置於每個類對象中,而static數據成員被置於類對象以外。static與nonstatic函數也都放在類對象以外,而對於virtual 函數,則經過虛函數表+虛指針來支持:htm
原則:
對普通單繼承而言
1 #include <iostream> 2 using namespace std; 3 4 class Base 5 { 6 public: 7 virtual void fun1(){ cout << "Base fun1" << endl; } 8 virtual void fun2(){ cout << "Base fun2" << endl; } 9 private: 10 int a; 11 }; 12 13 class Derive : public Base 14 { 15 public: 16 void fun2(){ cout << "Derive fun2" << endl; } 17 virtual void fun3(){} 18 private: 19 int b; 20 }; 21 22 int main() 23 { 24 Base b; 25 Derive d; 26 Base *p = &d; 27 p->fun1(); 28 p->fun2(); 29 30 system("pause"); 31 return 0; 32 }
輸出:
調試:
對象模型:
事實上vs調試並不能看到完整信息(好比virtual fun3以及以後提到到虛基類指針),正確的應該是
這裏講的是不考慮菱形繼承的多繼承,由於菱形繼承須要用到虛繼承,放到以後考慮
原則:
1 #include <iostream> 2 using namespace std; 3 4 class Base1 5 { 6 public: 7 virtual void fun1(){} 8 private: 9 int m_base1; 10 }; 11 12 class Base2 13 { 14 public: 15 virtual void fun1(){} 16 virtual void fun2(){} 17 private: 18 int m_base2; 19 }; 20 21 class Derive : public Base1,public Base2 22 { 23 public: 24 void fun1(){} 25 virtual void fun3(){} 26 private: 27 int m_derive; 28 }; 29 30 int main() 31 { 32 Base1 b1; 33 Base2 b2; 34 Derive d; 35 36 cout <<"b1:" <<sizeof(b1) << endl; 37 cout << "b2:" << sizeof(b2) << endl; 38 cout <<"d:" << sizeof(d) << endl; 39 system("pause"); 40 return 0; 41 }
輸出:
各個類對象的大小
調試:注意觀察fun1
對象模型:
原則:
虛繼承解決了菱形繼承中最派生類擁有多個間接父類實例的狀況
在C++對象模型中,虛繼承而來的子類會生成一個隱藏的虛基類指針(vbptr),在Microsoft Visual C++中,虛基類表指針老是在虛函數表指針以後,於是,對某個類實例來講,若是它有虛基類指針,那麼虛基類指針可能在實例的0字節偏移處(該類沒有vptr時,vbptr就處於類實例內存佈局的最前面,不然vptr處於類實例內存佈局的最前面),也可能在類實例的4字節偏移處。
虛基類表也由多個條目組成,條目中存放的是偏移值。
第一個條目存放虛基類表指針(vbptr)所在地址到該類內存首地址的偏移值
第2、第三...個條目依次爲該類的最左虛繼承父類、次左虛繼承父類...的內存地址相對於虛基類表指針的偏移值。
1 #include <iostream> 2 using namespace std; 3 4 class Base 5 { 6 public: 7 virtual void fun1(){} 8 virtual void fun2(){} 9 private: 10 int m_base; 11 }; 12 13 class Derive : virtual public Base 14 { 15 public: 16 void fun1(){} 17 virtual void fun3(){} 18 private: 19 int m_derive; 20 }; 21 22 int main() 23 { 24 Base b; 25 Derive d; 26 27 system("pause"); 28 return 0; 29 }
對象模型:
菱形虛繼承是多繼承和虛繼承的複合,直接畫一個對象模型吧:
筆記原圖: