一個類的實例化對象所占空間的大小? 注意不要說類的大小,是類的對象的大小。 首先,類的大小是什么?確切的說,類只是一個類型的定義,它是沒有大小可言的,用sizeof運算符對一個類型名操作,得到的是具有該類型實體的大小
1 #include <iostream>
2
3 using namespace std; 4
5 class A 6 { 7 }; 8
9 int main() 10 { 11 A obj; 12 int nLen = sizeof(obj); 13 cout << nLen << endl; //sizeof(一個空類)為什么等於1?
14
15 return 0; 16 }
可以看到一個空類對象的大小1.
一個空類對象的大小是1,為什么不是0?
初學者肯定會很煩惱?類A明明是空類,它的大小應該為0,為什么編譯器輸出的結果為1呢?這就是實例化的原因(空類同樣被實例化),每個實例在內存中都有一個獨一無二的地址,為了達到這個目的,編譯器往往會給一個空類隱含的加一個字節,這樣空類在實例化后在內存中得到了獨一無二的地址,所以obj的大小是1.
打斷點調試的時候,選中obj,然后按快捷鍵shift+F9
可以看到obj的地址是0x0019f77b;然后點擊vs菜單欄上的窗口----內存-----內存(1)
然后把obj的內存地址粘貼過來:
1 #include <iostream>
2
3 using namespace std; 4
5 class A 6 { 7 public: 8 void func1() { }; 9 void func2() { }; 10 void func3() { }; 11 }; 12
13 int main() 14 { 15 A obj; 16 int nLen = sizeof(obj); 17 cout << nLen << endl; //sizeof(一個空類)為什么等於1?
18
19 return 0; 20 }
此時給類A添加了三個成員函數,此時的類A對象的大小是多少呢?
我們看到此時類A對象obj的大小還是1,說明類的成員函數不占用類對象的內存空間
1 #include <iostream>
2
3 using namespace std; 4
5 class A 6 { 7 public: 8 void func1() { }; 9 void func2() { }; 10 void func3() { }; 11 char ab; 12 }; 13
14 int main() 15 { 16 A obj; 17 int nLen = sizeof(obj); 18 cout << nLen << endl; // 19
20 obj.ab = 'c'; 21 return 0; 22 }
我們添加了類A的成員變量ab之后,類A對象的大小是多大呢?
我們看到類A對象obj的大小是1.shift+F9我們獲取到obj的內存地址
當斷點走過obj.ab = 'c'這條賦值語句之后
可以看到原來0bj的內存地址上存儲了63這個十六進制,而十六進制63對應的ASCII碼剛好是字符c,所以說是一個字節大小。同時也說明了成員變量占用類對象的內存空間
1 #include <iostream>
2
3 using namespace std; 4
5 class A 6 { 7 public: 8 void func1() { }; 9 void func2() { }; 10 void func3() { }; 11 //char ab;
12 int nab; 13 }; 14
15 int main() 16 { 17 A obj; 18 int nLen = sizeof(obj); 19 cout << nLen << endl; //
20
21 //obj.ab = 'c';
22 obj.nab = 12; 23 return 0; 24 }
輸出結果為:
打斷點看到obj的內存地址是0x00cff9b4。而我們可以看到類A的成員變量是占用了4個字節來存儲數據的。
我們再來看一個例子:
1 #include <iostream>
2
3 using namespace std; 4
5 class A{}; 6 class B{}; 7 class C : public A 8 { 9 virtual void func() = 0; 10 }; 11
12 class D : public B, public C 13 { 14
15 }; 16
17 int main() 18 { 19 cout << sizeof(A) << endl; 20 cout << sizeof(B) << endl; 21
22 cout << sizeof(C) << endl; 23 cout << sizeof(D) << endl; 24
25 return 0; 26 }
輸出結果為:
1 #include <iostream> 2 3 using namespace std; 4 5 class A{}; 6 class B1{}; 7 class B 8 { 9 char c; 10 }; 11 class C : public A 12 { 13 virtual void func() = 0; 14 }; 15 16 class D : public B, public C {}; 17 18 class E :public B1, public C {}; 19 int main() 20 { 21 cout << sizeof(A) << endl; 22 cout << sizeof(B) << endl; 23 24 cout << sizeof(C) << endl; 25 cout << sizeof(D) << endl; 26 cout << sizeof(E) << endl; 27 28 return 0; 29 }
輸出結果:
類A,B的大小為1上面我們已經講過原因,而類C是由類A派生出來的,它里面有一個純虛函數,由於有虛函數的原因,有一個指向虛函數的指針(vptr),在32位的系統分配給指針的大小為4個字節,所以最后得到類C的大小為4個字節(類里只要有一個虛函數,或者說至少有一個虛函數,這個類就會產生一個指向虛函數的指針,有兩個虛函數就會產生兩個指向虛函數的指針,類本身,指向虛函數的指針(一個或者一堆)要有地方存放,這些指針就存放在一個表格里,這個表格我們稱為“虛函數表”,這個虛函數表是保存在可執行文件中的,在程序執行的時候載入到內存中來。不管有幾個虛函數,在32位的系統sizeof()都是多了4個字節)
類D的大小更讓初學者疑惑,類D是由類B,C派生而來的,它的大小應該為二者之和5,為什么是8呢?這是因為為了提高實例在內存中的存取效率,類的大小往往被調整到系統的整數倍,並采取就近的法則,離哪個最近的倍數,就是該類的大小,所以類D的大小為8個字節。
下面我們再看一個例子:
1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 private: 8 int data; 9 }; 10 11 class B 12 { 13 private: 14 int data; 15 static int xs; 16 }; 17 int B::xs = 10; 18 int main() 19 { 20 cout << sizeof(A) << endl; 21 cout << sizeof(B) << endl; 22 23 return 0; 24 }
輸出結果為:
為什么類B比類A多了一個數據成員,大小卻和類A的大小相同呢?因為類B的靜態數據成員被編譯器放在程序的一個global data members中,它是類的一個數據成員,但是它不影響類的大小,不管這個類實際產生了多少實例,還是派生了多少新的類,靜態成員在類中只有一個實體存在,而類的非靜態數據成員只有被實例化的時候,他們才存在,但是類的靜態數據成員一旦被聲明,無論類是否被實例化,它都已經存在,可以這么說,類的靜態數據成員是一種特殊的全局變量。
下面我們看一個有構造函數,和析構函數的類的大小,它又是多大呢?
1 #include <iostream>
2
3 using namespace std; 4
5 class A 6 { 7 public: 8 A(int a) { x = a; } 9 void func() 10 { 11 cout << x << endl; 12 } 13
14 ~A() { } 15 private: 16 int x; 17 int g; 18 }; 19
20 class B 21 { 22 public: 23 private: 24 int a; 25 int b; 26 static int xs; 27 }; 28
29 int B::xs = 20; 30
31 int main() 32 { 33 A a(10); 34 //a.func();
35
36 B b; 37 cout << sizeof(a) << endl; 38 cout << sizeof(b) << endl; 39 return 0; 40 }
它們的結果均相同,可以看出類的大小與它當中的構造函數,析構函數,以及其他的成員函數無關,只與它當中的成員數據有關.
從以上幾個例子我們可以總結出來類的大小
1.為類的非靜態成員數據的類型大小之和.
2.由編譯器額外加入的成員變量的大小,用來支持語言的某些特性(如:指向虛函數的指針).
3.為了優化存取效率,進行的邊緣調整(字節對齊).
4 與類中的構造函數,析構函數以及其他的成員函數無關.
另外:一個類對象至少占用1個字節的內存空間。