類的大小是什么?確切的說,類只是一個類型定義,它是沒有大小可言的。 用sizeof運算符對一個類型名操作,得到的是具有該類型實體的大小。首先:我們要知道什么是類的實例化,所謂類的實例化就是在內存中分配一塊地址
用sizeof對類名操作,得到的結果是該類的對象在存儲器中所占據的字節大小,由於靜態成員變量不在對象中存儲,因此這個結果等於各非靜態數據成員(不包括成員函數)的總和加上編譯器額外增加的字節。后者依賴於不同的編譯器實現,C++標准對此不做任何保證。
確定類大小的幾個原則:
- 為類的非靜態成員數據的類型大小之和
- 有編譯器額外加入的成員變量的大小,用來支持語言的某些特性(如:指向虛函數的指針)
- 為了優化存取效率,進行的邊緣調整
- 與類中的構造函數,析構函數以及其他的成員函數無關
下面分情況討論:
編譯器:vs2013
1. 空類
#include <iostream> using namespace std; class A{}; int main() { cout << sizeof(A) << endl; system("pause"); }
輸出:1
C++標准規定類的大小不為0,空類的大小為1,當類不包含虛函數和非靜態數據成員時,其對象大小也為1。這就是我們剛才所說的實例化的原因(空類同樣可以被實例化),每個實例在內存中都有一個獨一無二的地址,為了達到這個目的,編譯器往往會給一個空類隱含的加一個字節,這樣空類在實例化后在內存得到了獨一無二的地址
2. 簡單類
使用sizeof求這種簡單類,結果和求結構體的sizeof是一樣的,需要考慮偏移和對齊。要注意的是static變量不屬於類的一部分,如果類中定義了static變量,求sizeof時可以忽略它們。
#include <iostream> using namespace std; class A { int a; }; class B { char a; }; class C { int a; char b; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; cout << sizeof(C) << endl; system("pause"); }
輸出:
4 sizeof(int)
1 sizeof(char)
8 sizeof(int) + sizeof(char)(考慮對齊)
3. 帶虛函數的類
虛函數放在虛表中,類中定義了虛函數,需要存放一個指向虛表的指針
如果在類中聲明了虛函數(不管是1個還是多個),那么在實例化對象時,編譯器會自動在對象里安插一個指針指向虛函數表VTable,在32位機器上,一個對象會增加4個字節來存儲此指針,它是實現面向對象中多態的關鍵。而虛函數本身和其他成員函數一樣,是不占用對象的空間的
#include <iostream> using namespace std; class A { int a; virtual void fun(){} virtual void fun1(){} virtual void fun3(){} }; int main() { cout << sizeof(A) << endl; system("pause"); }
輸出
8 sizeof(int) + sizeof(虛表指針)
4. 普通繼承(父類不含虛函數)
#include <iostream> using namespace std; class A { int num; char str; }; class B : public A { char str2; int num2; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; system("pause"); }
輸出:
8 sizeof(類A)
16 sizeof(類A) + sizeo(類B)
一般來說,普通繼承的空間計算結果應當是sizeof(基類)+sizeof(派生類),然后考慮對齊,內存空間必須是類中數據成員所占用最大空間的整數倍。不過這是一般情況,具體怎么算要看編譯器,codeblocks把類B看成12,因為把str2和str放在一起了
5.普通繼承(父類含虛函數)
#include <iostream> using namespace std; class A { int num; virtual void fun(){} }; class B : public A { int num2; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; system("pause"); }
輸出:
8 sizeof(類A)
12 sizeof(類A) + sizeo(類B)
6. 普通繼承(含虛函數的子類普通繼承含虛函數的父類)
這個要注意的一點是,雖然子類和父類都包含虛函數, 但它們存放於同一個虛表中,因此只需要一個指針
#include <iostream> using namespace std; class A { int num; virtual void fun(){} }; class B : public A { int num2; virtual void fun1(){} }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; system("pause"); }
輸出:
8 sizeof(int) + sizeof(指針)
12 sizeof(int) + sizeof(int) + sizeof(指針) (繼承后只有一個虛表)
7. 子類虛繼承父類
sizeof(子類)=sizeof(基類)+sizeof(虛表指針)+sizeof(子類數據成員) 此外,如果子類和基類都有虛函數,各自用各自的虛表
#include <iostream> using namespace std; class A { int num; }; class B : virtual public A { int num2; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; system("pause"); }
輸出:
4
12 sizeof(A) + sizeof(B) + sizeof(虛繼承指針)
#include <iostream> using namespace std; class A { int num; virtual void fun(){} }; class B : virtual public A { int num2; virtual void fun1(){} }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; system("pause"); }
輸出:
8
20 sizeof(A) + sizeof(B) + sizeof(虛繼承指針) + sizeof(A類虛表指針) + sizeof(B類虛表虛指針)
8. 多重虛繼承
虛繼承存在的意義就是為了減少內存開銷和二義性,實現對象共享。
#include <iostream> using namespace std; class A { int num; }; class B : virtual public A { int num2; }; class C : virtual public A { int num3; }; class D : public B, public C { int num4; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; cout << sizeof(C) << endl; cout << sizeof(D) << endl; system("pause"); }
輸出:
4
12
12
24
D中包含a,b,c,d四個數據成員,還包含兩個指向虛基類A的指針,這種情況下,VS和CB都沒有將兩個指針合為一個。這種情況也可以這樣考慮,sizeof(D)=sizeof(B)+sizeof(C),但由於是虛繼承,虛基類A中數據成員a只需要保留一份,而我們算了兩次,所以還需要減去A的數據成員,所以公式應當是sizeof(D)=sizeof(D的非靜態數據成員) + sizeof(B)+sizeof(C)-sizeof(A的非靜態數據成員)。
參考資料:http://blog.csdn.net/szchtx/article/details/10254007