1.类/结构体的大小主要受以下因素影响
1.1 成员变量
成员变量会直接影响类的大小,类的大小通常是其成员变量大小之和(但会受到对齐规则影响)
不同类型的成员变量占用的空间不同,编译器会为它们分配适当的内存。
函数成员不算作类或结构体的成员变量,也不会影响类的大小。
类的大小只由其非静态成员变量、对齐规则和**虚函数表指针(如果有虚函数)**等因素决定
1.2 对齐规则
C++ 中通常使用 内存对齐 来提高内存访问效率。
对齐会引入 填充字节(padding),导致类的实际大小可能大于所有成员变量大小之和。
常见对齐规则:
struct Example {
char a;
int b;
};
- 每个成员变量的地址必须是其类型大小的倍数。
- 类的大小必须是最大对齐边界的倍数。
在设置了内存对齐之后会有不同的结果
#include<iostream>
#pragma pack(push, 1)
class Example1
{
int a;
char b;
};
#pragma pack(pop)
int main() {
std::cout << sizeof(Example1);
return 0;
}
1.3 虚函数
- 如果类定义了虚函数,编译器会为类添加一个虚函数表指针(vptr),这通常会增加类的大小。
vptr 的大小与指针大小相同(通常为 4 字节或 8 字节,取决于平台)
class Example
{
int a;
char b;
virtual void test1() {
}
};
class Example_1
{
int a;
char b;
void test1() {
}
};
sizeof(Example);
sizeof(Example_1);
1.4 继承
- 基类的成员变量会影响派生类的大小。
- 如果基类有虚函数,派生类会继承虚函数表指针。
- 多继承时,每个基类的虚函数表可能需要单独的指针
1.5 编译器优化
- 不同的编译器可能会对类的布局进行优化,影响类的大小。
- 使用编译器选项(如
#pragma pack)可以改变类的对齐方式。
2. 空类的大小
class Empty {};
- 空类的大小不会是 0 字节,而是至少为 1 字节。
- 原因:为了确保每个对象有唯一的地址(C++ 标准规定),即使类不包含任何数据。
- 实际上,这个 1 字节没有数据含义,只是为了让空类实例可区分。
#include <iostream>
class Empty {};
int main() {
Empty e1, e2;
std::cout << "Size of Empty class: " << sizeof(Empty) << std::endl;
std::cout << "Address of e1: " << &e1 << ", e2: " << &e2 << std::endl;
return 0;
}
Size of Empty class: 1
Address of e1: 0x7ffee49214c0, e2: 0x7ffee49214c1
3.为什么函数不影响类的大小
- 函数代码存储在代码段:类的成员函数是代码,它们存储在可执行文件的代码段中,而不是类的对象中。
- 成员函数通过隐式指针调用:成员函数的访问通过隐式的
this 指针操作类的成员变量,this 指向具体的对象,函数本身并不占用对象的内存。
4.静态成员变量和函数也不影响类的大小
静态成员函数没有隐式的 this 指针,不与具体对象关联,因此它们也不影响类的大小
静态成员变量的存储位置:
- 静态成员变量是属于类本身,而不是属于类的某个对象。
- 它们被存储在全局数据区(通常是静态存储区或 BSS/数据段),与类的对象无关。
- 因此,静态成员变量不会包含在对象实例中,也不会影响类的大小。
class MyClass {
int a;
static int staticVar;
static void test1(){
}
};
int main() {
MyClass obj;
std::cout << sizeof(obj) << std::endl;
return 0;
}