在 C++中,类(或结构体)的大小主要由以下几个因素决定:
1. 非静态成员变量
类的大小首先由其非静态数据成员的总大小决定。
成员变量的排列顺序和类型直接影响内存布局(可能因对齐填充而增大)。
静态成员变量(static
)不占用类实例的大小,因为它们存储在全局/静态存储区。
2. 内存对齐(Alignment Padding)
为了满足硬件对齐要求,编译器可能在成员之间或末尾插入填充字节(Padding)。
对齐规则包括:
每个成员的对齐要求:由成员自身的类型决定(例如 int
需要 4 字节对齐,double
需要 8 字节对齐)。
结构体的总对齐值:由所有成员中最大的对齐值决定(假如有 int 和 double,则是 double
的 8 字节对齐)。
总大小必须是结构体对齐值的整数倍(即 8 的倍数)。
例如:
struct A
{
double a;
int b;
int c;
}
sizeof(A) = 16
大部分编译器输出都是 16
把顺序换一下:
struct A
{
int b;
double a;
int c;
}
sizeof(A) = 24
大部分编译器输出是 24
可通过 alignas
手动指定对齐方式,或使用 #pragma pack
修改对齐规则(可能影响性能)。
3. 虚函数(动态多态)
如果类包含虚函数,编译器会为其添加一个虚函数表指针(vptr),通常占用 sizeof(void*)
(32 位系统为 4 字节,64 位为 8 字节)。
虚继承(virtual
继承)可能引入额外的开销(如虚基类指针)。
4. 继承关系
派生类的大小包括基类的所有非静态成员(可能因继承方式不同而调整)。
多继承或虚继承可能导致额外的指针或偏移量。
5. 空类的大小
空类(无成员、无虚函数)的大小通常为 1 字节,用于确保不同实例有唯一地址。
如果作为基类,可能被优化为 0 字节(空基类优化,EBCO)。
6. 编译器优化
某些编译器可能合并填充区域或优化布局(如 -O3
下的优化)。
C++11 的 final
或 override
等关键字不影响大小。
示例代码
#include <iostream>
using namespace std;class A {}; // 空类,大小=1class B {int x; // 4字节char c; // 1字节// 填充3字节(假设默认对齐为4)
}; // 大小=8class C {virtual void foo() {} // 虚函数指针:8字节(64位系统)int x; // 4字节// 填充4字节(对齐到8)
}; // 大小=16int main() {cout << "A: " << sizeof(A) << endl; // 1cout << "B: " << sizeof(B) << endl; // 8cout << "C: " << sizeof(C) << endl; // 16return 0;
}
关键点总结
成员变量是主要因素,静态成员不计算在内。
对齐规则可能导致额外填充。
虚函数引入虚表指针开销。
继承可能增加基类成员和额外信息。
空类最小为 1 字节。
可以通过 sizeof
运算符验证类的大小,或使用 offsetof
宏检查成员偏移量。