C++一個類對象的大小計算
計算一個類對象的大小時的規律:
1、空類、單一繼承的空類、多重繼承的空類所占空間大小為:1(字節,下同);
2、一個類中,虛函數本身、成員函數(包括靜態與非靜態)和靜態數據成員都是不占用類對象的存儲空間的;
3、因此一個對象的大小≥所有非靜態成員大小的總和(包括動態分配的變量...);
4、當類中聲明了虛函數(不管是1個還是多個),那麽在實例化對象時,編譯器會自動在對象裏安插一個指針vPtr指向虛函數表VTable;
5、虛繼承的情況:虛繼承的實現是通過一個虛基類指針列表;由於涉及到虛函數表和虛基表,會同時增加一個(多重虛繼承下對應多個)vfPtr指針指向虛函數表vfTable和一個vbPtr指針指向虛基表vbTable,這兩者所占的空間大 小為:8(或8乘以多繼承時父類的個數);
6、在考慮以上內容所占空間的大小時,還要註意編譯器下的“補齊”padding的影響,即編譯器會插入多余的字節補齊;
7、類對象的大小=各非靜態數據成員(包括父類的非靜態數據成員但都不包括所有的成員函數)的總和+ vfptr指針(多繼承下可能不止一個)+vbptr指針(多繼承下可能不止一個)+編譯器額外增加的字節。
非虛單繼承實例如下:
class A { char k[3]; public: virtual void aa() {
}; }; |
class B:public A { char j[3]; public: virtual void bb() {
}; }; |
class C:public B { char i[3]; public: virtual void cc() {
}; }; |
//一個字節 按4字節對齊 |
||
4(指向虛函數的虛指針)+4(自己的數據成員) = 8 |
4(指向虛函數的虛指針)+4(自己的數據成員)+4(父類A的數據成員)=12 |
4(指向虛函數的虛指針)+4(自己的數據成員)+8(父類的數據成員)=16 |
非虛多繼承實例如下:
|
|
重寫函數f() Base -->char 類型 Base1:4(指向虛函數的虛指針)+4(自己的數據成員) = 8 Base2:4(指向虛函數的虛指針)+4(自己的數據成員) = 8 Base3:4(指向虛函數的虛指針)+4(自己的數據成員) = 8 Derive: 4(Base1指向虛函數表的虛指針)+4(Base1繼承的成員變量)+4(Base2指向虛函數表的虛指針)+4(Base2繼承的成員變量)+3(Base1指向虛函數表的虛指針)+4(Base3繼承的成員變量)+4(自己的數據成員)=28 |
1)虛函數表在最前面的位置。 2)成員變量根據其繼承和聲明順序依次放在後面。 3)每個父類都有自己的虛表。 4)子類的成員函數被放到了第一個父類的表中。 5)內存布局中,其父類布局依次按聲明順序排列。 6)每個父類的虛表中的f()函數都被overwrite成了子類的f()。這樣做就是為了解決不同的父類類型的指針指向同一個子類實例,而能夠調用到實際的函數。 |
非虛繼承的鉆石繼承:
|
紅色的部分就是重復的部分,就會造成二義性 |
虛繼承的鉆石繼承:
|
可以看出,少了重合的部分。但是,代價是增加了一個虛基類指針vbptr。 |
C++一個類對象的大小計算