很多C++书籍中都介绍过,一个Class对象需要占用多大的内存空间。最权威的结论是:
*非静态成员变量总合。(not static)
*加上编译器为了CPU计算,作出的数据对齐处理。(c语言中面试中经常会碰到内存对齐的问题)
*加上为了支持虚函数(virtual function),产生的额外负担。
下面给出几个程序来看一下:
#include <iostream> #include <cstdio> #include <string> using namespace std; class Car1{ }; void fun1(void) { int size =0; Car1 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car1 Size",size); } class Car2{ private: int nLength; int nWidth; }; void fun2(void) { int size = 0; Car2 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car2 Size",size); } class Car3{ private: int nLength; int nWidth; static int sHight; }; void fun3(void) { int size =0; Car3 objCar; size =sizeof(objCar); printf("%s is %d\n","Class Car3 Size",size); } class Car4{ private: char chLogo; int nLength; int nWidth; static int sHigh; }; void fun4(void) { int size =0; Car4 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car4 Size",size); } class Car5{ public: Car5(){}; ~Car5(){}; public: void Fun(){}; }; void fun5(void) { int size =0 ; Car5 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car5 Size",size); } class Car6{ public: Car6(){}; ~Car6(){}; public: void Fun(){}; private: int nLength; int nWidth; }; void fun6(void) { int size = 0; Car6 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car6 Size",size); } class Car7{ public: Car7(){}; virtual ~Car7(){}; public: void Fun(){}; }; void fun7(void) { int size = 0; Car7 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car7 Size",size); } class Car8{ public: Car8(){}; virtual ~Car8(){}; public: void Fun(){}; virtual void Fun1(){} }; void fun8(void) { int size = 0; Car8 objCar; size = sizeof(objCar); printf("%s is %d\n","Class Car8 Size",size); } int main() { fun1(); fun2(); fun3(); fun4(); fun5(); fun6(); fun7(); fun8(); }
编译:g++ memAlign.cpp -o memAlign
输出结果:
Class Car1 Size is 1 Class Car2 Size is 8 Class Car3 Size is 8 Class Car4 Size is 12 Class Car5 Size is 1 Class Car6 Size is 8 Class Car7 Size is 8Class Car8 Size is 8
ps:上述编译环境是在mac os下的g++
下面我们具体来分析一下这几个情况:
1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;
3、因此一个对象的大小≥所有非静态成员大小的总和;
4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
5、虚继承的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr(virtual function table)指针指向虚函数表vfTable和一个vbPtr(virtual base pointer)指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);
6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;(请参考《c和指针》)
7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。
在VC 6.0中,结果是
Class Car1 Size is 1 Class Car2 Size is 8 Class Car3 Size is 8 Class Car4 Size is 12 Class Car5 Size is 1 Class Car6 Size is 8 Class Car7 Size is 4 Class Car8 Size is 4
主要的不同点是:在Car7和Car8,在VC 6.0中虚函数指针占用4个字节,在gcc编译器中占用8个字节。
也可以换一种说法是virtual函数指针在VC下以4字节对齐,在gcc下是8字节对齐,这样解释就比较清楚了。
二、编程实现成员在类或结构体中的偏移量
代码如下所示:
1 #include <cstdio> 2 #include <iostream> 3 #define pos(type,member) (&((type *)0)->member) 4 5 class car{ 6 public: 7 car(){} 8 ~car(){} 9 public: 10 virtual void fun(){} 11 private: 12 int c; 13 public: 14 void print() 15 { 16 printf("%x\n",pos(car,c)); 17 } 18 }; 19 20 int main() 21 { 22 struct Node{ 23 int a ; 24 char b; 25 int c; 26 }; 27 car objCar; 28 // printf("%x\n",&((struct Node *)0)->b); 29 printf("%x\n",pos(struct Node,b)); 30 printf("%x\n",pos(struct Node,c)); 31 // printf("%x\n",pos(class car,c)); 32 objCar.print(); 33 return 0; 34 }
其中关键的是找到函数能够实现计算成员在类中的偏移量,这里用了宏来实现的。
#define pos(type,member) (&((type *)0)->member)
(从地址0开始的一个type结构体或者类,其成员的地址就是成员所在类或结构体的偏移量)
上述程序的输出结果就是:4 8 8