C++ 中,结构体(struct)类(class)的大小受到多种因素的影响,主要包括成员变量、对齐规则、继承关系等

1.类/结构体的大小主要受以下因素影响

1.1 成员变量

成员变量会直接影响类的大小,类的大小通常是其成员变量大小之和(但会受到对齐规则影响)

不同类型的成员变量占用的空间不同,编译器会为它们分配适当的内存。

函数成员不算作类或结构体的成员变量,也不会影响类的大小。

类的大小只由其非静态成员变量对齐规则虚函数表指针(如果有虚函数)等因素决定

1.2 对齐规则

C++ 中通常使用 内存对齐 来提高内存访问效率。

对齐会引入 填充字节(padding),导致类的实际大小可能大于所有成员变量大小之和。

常见对齐规则:

struct Example {
    char a;     // 1 字节
    int b;      // 4 字节
};              // 实际大小为 8 字节(对齐填充)
​
  • 每个成员变量的地址必须是其类型大小的倍数。

  • 类的大小必须是最大对齐边界的倍数。

在设置了内存对齐之后会有不同的结果

#include<iostream>
​
#pragma pack(push, 1) // 设置为 1 字节对齐
class Example1
{
    int a; //4
    char b;//1
};
#pragma pack(pop) // 恢复默认对齐
​
int main() {
    std::cout << sizeof(Example1); //5
    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);    // 64位为16  32位为12(虚表指针收到平台的影响)
sizeof(Example_1);  // 没有虚表指针 为8

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; // 仅包含非静态成员变量的大小 4
    return 0;
}
​