转自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