C++中类和结构体的空间大小分配有以下几个原则:
• 类和结构体的成员变量在内存中是按照声明的顺序依次存放的,但是可能会有一些空洞(padding)出现,以满足对齐要求。
• 对齐要求是指每个成员变量的起始地址要能够被其自身类型所占字节数的整数倍整除,如果不满足则需要在前面补充一些空字节。
• 对齐要求的目的是为了提高内存访问效率,不同的编译器和目标平台可能有不同的默认对齐值,一般是4字节或8字节,也可以通过#pragma pack指令来修改。
• 类和结构体本身也要满足对齐要求,即整个类或结构体的大小要能够被其最大成员变量所占字节数的整数倍整除,如果不满足则需要在末尾补充一些空字节。
• 类和结构体中的成员函数(包括构造函数、析构函数、静态函数、虚函数等)不占用对象的内存空间,而是存放在代码区,因此不影响类或结构体的大小。
• 类和结构体中的静态成员变量也不占用对象的内存空间,而是存放在静态区,因此不影响类或结构体的大小。
• 类和结构体中如果有虚函数,则会额外增加一个指向虚函数表(vtable)的指针(vptr),该指针占用4字节或8字节(取决于目标平台),并且通常放在对象的首地址处。
举例说明:
//定义一个结构体
struct A {
char a; //1字节
int b; //4字节
short c; //2字节
double d; //8字节
};
//结构体A中,按照声明顺序依次存放成员变量,但是需要满足对齐要求:
//a占用第0单元,b需要从第4单元开始(因为4能被4整除),所以第1、2、3单元为空洞;
//b占用第4、5、6、7单元,c需要从第8单元开始(因为8能被2整除),所以没有空洞;
//c占用第8、9单元,d需要从第16单元开始(因为16能被8整除),所以第10、11、12、13、14、15单元为空洞;
//d占用第16、17、18、19、20、21、22、23单元,没有更多成员变量了;
//结构体A本身也需要满足对齐要求,即其大小要能被其最大成员变量d所占字节数8的整数倍整除;
//由于24能被8整除,所以不需要在末尾补充空字节;
//因此,结构体A的大小为24字节。
//定义一个类
class B {
public:
B() {} //构造函数
~B() {} //析构函数
void f1() {} //普通成员函数
static void f2() {} //静态成员函数
virtual void f3() {} //虚函数
private:
char e; //1字节
int f; //4字节
short g; //2字节
double h; //8字节
static int i; //静态成员变量
};
//计算结构体A和类B的大小
int main() {
cout << sizeof(A) << endl; //输出24
cout << sizeof(B) << endl; //输出32(假设目标平台为64位)
return 0;
}
//类B中,按照声明顺序依次存放成员变量,但是需要满足对齐要求:
//由于类B中有虚函数,所以会增加一个指向虚函数表的指针vptr,该指针通常放在对象的首地址处;
//假设目标平台为64位,那么vptr占用8字节,从第0单元开始,占用第0、1、2、3、4、5、6、7单元;
//e占用第8单元,f需要从第12单元开始(因为12能被4整除),所以第9、10、11单元为空洞;
//f占用第12、13、14、15单元,g需要从第16单元开始(因为16能被2整除),所以没有空洞;
//g占用第16、17单元,h需要从第24单元开始(因为24能被8整除),所以第18、19、20、21、22、23单元为空洞;
//h占用第24、25、26、27、28、29、30、31单元,没有更多成员变量了;
//类B本身也需要满足对齐要求,即其大小要能被其最大成员变量h所占字节数8的整数倍整除;
//由于32能被8整除,所以不需要在末尾补充空字节;
//因此,类B的大小为32字节。
//类B中的构造函数、析构函数、普通成员函数、静态成员函数和虚函数都不占用对象的内存空间,而是存放在代码区;
//类B中的静态成员变量i也不占用对象的内存空间,而是存放在静态区;
//这些都不影响类B的大小。
c++类中的大小与成员属性以及成员函数的关系?
一般来说,类中的成员属性(也就是数据成员)会占用对象的内存空间,而成员函数(也就是方法)则不会。这是因为成员属性是每个对象独有的,它们的值可能不同,所以需要为每个对象分配内存来存储它们。而成员函数是所有对象共享的,它们的代码只需要在内存中存储一份,然后通过指针来调用即可。
但是,这里有一些特殊情况需要注意:
• 如果类中有静态数据成员,那么它不会占用对象的内存空间,而是在全局数据区分配内存,所有对象共享同一个静态数据成员。
• 如果类中有虚函数,那么每个对象会多占用一个指针的大小的内存空间,用来存储虚函数表的地址。虚函数表是一个包含了该类所有虚函数地址的数组,它在程序运行时动态绑定虚函数的调用。
• 如果类中有继承或多态的关系,那么类的大小还要考虑基类和派生类之间的内存布局和对齐方式,这可能会导致一些额外的填充字节产生。
因此,如果想计算一个类的大小,需要知道它的数据成员的类型和数量,以及它是否有静态数据成员、虚函数、继承或多态等特性。然后你还需要根据编译器和平台的不同,确定它们的对齐规则和字节序。
注:空类的大小为1字节。