当前位置: 代码网 > it编程>编程语言>C/C++ > 详解C++中类的大小决定因数

详解C++中类的大小决定因数

2025年04月07日 C/C++ 我要评论
在 c++ 中,类的大小(sizeof(classname))受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等。以下是影响类大小的关键因素:1.非静态数据成员类的大小主要取决于其非静态

在 c++ 中,类的大小(sizeof(classname))受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等。以下是影响类大小的关键因素:

1. 非静态数据成员

类的大小主要取决于其非静态成员变量的大小。

  • 静态成员变量 不影响 类的大小(存储在全局数据区)。
  • 非静态成员变量 占用对象的存储空间。

示例:

class a {
    int x;   // 4 bytes
    char y;  // 1 byte (可能有填充)
};
std::cout << sizeof(a) << std::endl; // 可能是 8,而不是 5

由于结构体对齐(padding),sizeof(a) 可能是 8,而不是 5(见对齐规则)。

2. 数据对齐(padding)

编译器会对数据进行内存对齐,以提高 cpu 访问效率。

  • 变量的地址需要符合其对齐要求(如 int 需要 4 字节对齐)。
  • 可能会在成员之间填充字节(padding)。

示例:

class b {
    char a;   // 1 byte
    int b;    // 4 bytes, 但需要 4 字节对齐
    char c;   // 1 byte
};
  • char a 占 1 字节,但后面填充 3 字节(以满足 int b 的对齐)。
  • int b 占 4 字节。
  • char c 占 1 字节,但后面填充 3 字节(类的大小需要是 int 的倍数)。
  • 总大小 = 1 + 3 (填充) + 4 + 1 + 3 (填充) = 12 字节。

可以使用 #pragma pack(1) 强制取消对齐,但可能会影响性能:

#pragma pack(1) 
class c {
    char a;
    int b;
    char c;
};
#pragma pack()
std::cout << sizeof(c) << std::endl; // 可能是 6 而不是 12

3. 虚函数(vtable 指针)

如果类包含虚函数,则对象会存储一个指向虚函数表(vtable)的指针。

  • 该指针通常占 8 字节(64 位系统)或 4 字节(32 位系统)。
  • 即使没有成员变量,类也会占用指针大小的空间。

示例:

class d {
    virtual void func() {}
};
std::cout << sizeof(d) << std::endl; // 8(64 位系统),4(32 位系统)

仅包含虚函数的类,仍然占用 vtable 指针的大小。

如果是多重继承且基类有虚函数,每个基类都有自己的 vtable 指针:

class base1 {
    virtual void foo() {}
};

class base2 {
    virtual void bar() {}
};

class derived : public base1, public base2 {
};
std::cout << sizeof(derived) << std::endl; // 16(64 位系统),8(32 位系统)

derived 类有两个 vtable 指针,每个占 8 字节(64 位系统)。

4. 继承

  • 单继承:子类包含父类的成员,并可能继承 vtable 指针。
  • 多继承:可能会导致多个 vtable 指针,增加对象大小。
  • 虚继承:由于需要额外存储 虚基类指针(vbptr),可能增大对象大小。

普通继承

class base {
    int a;
};

class derived : public base {
    char b;
};
std::cout << sizeof(derived) << std::endl; // 8(对齐后)

虚继承

虚继承需要维护 虚基类表指针(vbptr),导致额外的存储开销:

class base {
    int a;
};

class derived1 : virtual public base {};
class derived2 : virtual public base {};

class final : public derived1, public derived2 {};
std::cout << sizeof(final) << std::endl; // 额外增加 vbptr

final 可能比普通继承的大小更大,因为 vbptr 需要额外存储虚基类地址。

5. 空类

空类在 c++ 里不是零大小,通常是 1 字节,以保证两个对象的地址不同:

class empty {};
std::cout << sizeof(empty) << std::endl; // 1

即使类为空,c++ 也会给它分配 1 字节,以确保不同对象有唯一地址。

如果空类有虚函数,它仍然包含 vtable 指针:

class emptyvirtual {
    virtual void func() {}
};
std::cout << sizeof(emptyvirtual) << std::endl; // 8(64 位系统)

6. 位域(bit fields)

位域允许多个变量共享同一个字节,减少存储空间:

class bitfield {
    unsigned int a : 1;  // 1 bit
    unsigned int b : 2;  // 2 bits
    unsigned int c : 3;  // 3 bits
};
std::cout << sizeof(bitfield) << std::endl; // 4(存储在一个 int 中)

位域大小取决于其基础类型(如 int),如果跨越边界,可能导致额外填充。

7. 编译器优化

不同编译器可能优化类的布局,如:

  • 调整成员变量顺序,减少填充字节。
  • 自动合并位域,提高空间利用率。

可以用 -wpadded(gcc/clang)检查填充:

g++ -wpadded myfile.cpp

总结

影响因素影响
非静态成员直接影响类大小
数据对齐可能增加填充字节
虚函数增加 vtable 指针(通常 8 字节)
继承继承成员、vtable,可能增加 vbptr
空类不是 0,而是 1 字节
位域可能优化存储,但依赖对齐
编译器优化可能调整成员顺序

如果你需要最小化类的大小,可以:

  • 调整成员变量顺序 以减少填充字节。
  • 避免不必要的虚函数(若无多态需求)。
  • 使用位域 以节省空间(但可能影响性能)。
  • 使用 #pragma pack 取消对齐(但可能降低访问速度)。

到此这篇关于详解c++中类的大小决定因数的文章就介绍到这了,更多相关c++类的大小决定因数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网! 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com