轉自https://blog.csdn.net/zhengjihao/article/details/77825269
其中有的描述與實際GCC上執行的結果不符,不過其中的分析思路需要掌握。
以下是GCC的實際執行結果:
1 #include <iostream> 2 using namespace std; 3 4 int *pointer; 5 6 class NullCls {}; 7 8 class NullVirtual { 9 public: 10 virtual void fun() {}; 11 virtual void fun2() {}; 12 }; 13 14 class CSI { 15 public: 16 char b; 17 short c; 18 int a; 19 }; 20 21 class CIS { 22 public: 23 char b; 24 int a; 25 short c; 26 }; 27 28 class CSV { 29 public: 30 char b; 31 short c; 32 virtual void fun() {}; 33 }; 34 35 class CVS { 36 public: 37 char a; 38 virtual void fun() {}; 39 short b; 40 }; 41 42 class StaticInt { 43 public: 44 char b; 45 virtual void fun() {}; 46 static int c; // 放在程序的global data members中 47 }; 48 49 class Method { 50 public: 51 void fun() {}; 52 }; 53 54 class Parent { 55 public: 56 int c; 57 }; 58 59 class Son : public Parent { 60 public: 61 int b; 62 }; 63 64 class Parent2 { 65 public: 66 int a; 67 char b; 68 }; 69 70 class Son2 : public Parent2 { 71 public: 72 char c; 73 }; 74 75 class A 76 { 77 virtual void fun() {} 78 }; 79 80 class B 81 { 82 virtual void fun2() {} 83 }; 84 85 class C : virtual public A, virtual public B 86 { 87 public: 88 virtual void fun3() {} 89 }; 90 91 class C2 : public A, public B 92 { 93 public: 94 virtual void fun3() {} 95 }; 96 97 void size(string name, int s) { 98 cout << name << "=" << s << endl; 99 } 100 101 int main(){ 102 size("pointer", sizeof(pointer)); // 8 103 size("NullCls", sizeof(NullCls)); // 1 104 size("NullVirtual", sizeof(NullVirtual)); // 8 105 size("CSI", sizeof(CSI)); // 8 106 size("CIS", sizeof(CIS)); // 12 107 size("CSV", sizeof(CSV)); // 16 108 size("CVS", sizeof(CVS)); // 16 109 size("StaticInt", sizeof(StaticInt)); // 16 110 size("Method", sizeof(Method)); // 1 111 size("Son", sizeof(Son)); // 8 112 size("Son2", sizeof(Son2)); // 12 113 size("C", sizeof(C)); // 16 114 size("C2", sizeof(C2)); // 16 115 }
《原文如下》
1空類
1 class A {};
大小為1。
類的實例化就是給每一個實例在內存中分配一塊地址。空類被實例化時,會由編譯器隱含的添加一個字節。所以空類的size為1。
2 虛函數
class A { public: virtual void fun() {}; virtual void fun2() {}; };
大小為4。
當C++類中有虛函數的時候,會有一個指向虛函數表(V-table)的指針,所有的虛函數都在這個表中。指針大小為4,所以size為4。
在來看如下代碼:
class A { public: char b; short c; int a; }; class B { public: char a; int c; short b; };
考慮數據對齊,大小分別為 8 和 12。如果我們將int換成虛函數,會是什么結果呢?
class A { public: char b; short c; virtual void fun() {} }; class B { public: char a; virtual void fun() {} short b; };
大小分別為 8 8。 都是占4個字節,結果不一樣。 這是因為,為了效率問題,編譯器(gcc 和 微軟)一般會把虛指針放在類的內存空間的最前面的位置,不管虛函數聲明的位置。考慮對齊,大小都是 4 +1+1+2 = 8.
3 靜態數據成員
class A { public: char b; virtual void fun() {}; static int c; };
大小為8。
靜態數據成員被編譯器放在程序的一個global data members中,它是類的一個數據成員,但不影響類的大小。不管這個類產生了多少個實例,還是派生了多少新的類,靜態數據成員只有一個實例。靜態數據成員,一旦被聲明,就已經存在。 考慮到數據對齊, 最終是8字節。
4 普通成員函數
class A { public: void fun() {}; };
大小為1。
類的大小與構造函數,析構函數,普通成員函數無關。
5 普通單繼承
class A { int c; }; class B : public A { int a; };
大小分別為4 和 8。 可以看到普通的繼承就是基類的大小+派生類自身的大小。注意數據對齊。
注意:類的數據成員按其聲明順序加入內存,無訪問權限無關,只看聲明順序。
class A { int a; char b; }; class C : public A { public: char c; };
上面這段代碼,不同的編譯器結果不同,VS的結果是 8 和 12, GCC是8 和 8。VS中 相當於
class C { A a; char c; };
A的大小為8,對齊值為4, 則考慮總體對齊 8 + 1 + 3(padding) = 12。
GCC 則是
class C { int a; char b; char c; };
結果為 4 + 1 + 1 + 2 = 8。【與實際執行有出入】
6 含虛函數的單繼承
class A { virtual void fun () {} }; class C : public A { public: virtual void fun2() {} };
大小分別為4 和 4。派生類繼承了基類的虛指針,所以大小為4。
7 虛單繼承
class A { virtual void fun () {} }; class C : virtual public A { public: virtual void fun2() {} };
這段代碼,VS和gcc結果不一樣。VS為 4 和 12。 gcc為4 和4。
8 普通多繼承
class A { int a; char b; }; class B { char b; }; class C : public A, public B { public: char c; };
VS:8 1 12
GCC:8 1 8
VS中相當於把A B當做整體看待, GCC則拆分整合。
9 虛函數多繼承
class A { virtual void fun() {} }; class B { virtual void fun2() {} }; class C : public A, public B { public: virtual void fun3() {} };
結果為 4 4 8。分析:類A一個虛函數表,類B一個虛函數表,類C繼承了兩個虛函數表,並把自己的虛函數寫在了繼承順序中第一個虛函數表中。
10 虛繼承
class A { virtual void fun() {} }; class B { virtual void fun2() {} }; class C : virtual public A, virtual public B { public: virtual void fun3() {} };
GCC: 4 4 8 VS:4 4 16