为什么没有成员变量的C ++类占用空间?

时间:2014-09-11 11:57:19

标签: c++ class sizeof predicate

我发现MSVC和GCC编译器每个类实例至少分配一个字节,即使该类是没有成员变量的谓词(或者只是静态成员变量)。以下代码说明了这一点。

#include <iostream>

class A
{
public:
   bool operator()(int x) const
   {
      return x>0;
   }
};

class B
{
public:
   static int v;
   static bool check(int x)
   {
      return x>0;
   }
};

int B::v = 0;

void test()
{
   A a;
   B b;
   std::cout << "sizeof(A)=" << sizeof(A) << "\n"
             << "sizeof(a)=" << sizeof(a) << "\n"
             << "sizeof(B)=" << sizeof(B) << "\n"
             << "sizeof(b)=" << sizeof(b) << "\n";
}

int main()
{
   test();
   return 0;
}

输出:

sizeof(A)=1
sizeof(a)=1
sizeof(B)=1
sizeof(b)=1

我的问题是为什么编译器需要它?我能提出的唯一原因是确保所有成员var指针不同,以便我们可以通过比较它们的指针来区分A或B类型的两个成员。但是在处理小型容器时,这个成本非常严重。考虑到可能的数据对齐,我们每个类最多可以获得16个字节而不需要vars(?!)。假设我们有一个自定义容器,通常会包含一些int值。然后考虑一系列这样的容器(大约有1000000个成员)。开销将是16 * 1000000!可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。另外,考虑到类实例应该总是占用一些空间,在调用A()(value)时应该期望什么类型的开销?

4 个答案:

答案 0 :(得分:74)

必须满足C ++标准中的不变量:相同类型的每个C ++对象都需要具有可识别的唯一地址。

如果对象没有占用空间,则数组中的项目将共享相同的地址。

答案 1 :(得分:26)

基本上,这是两个要求之间的相互作用:

  • 同一类型的两个不同对象必须位于不同的地址。
  • 在数组中,对象之间可能没有任何填充。

请注意,仅第一个条件不需要非零大小:给定

struct empty {};
struct foo { empty a, b; };

通过使用零大小a后跟单个填充字节来强制执行不同的地址,然后是零大小的b,可以轻松满足第一个要求。但是,给定

empty array[2];

不再有效,因为不允许在不同对象empty[0]empty[1]之间填充。

答案 2 :(得分:13)

所有完整对象必须具有唯一的地址;所以他们必须至少占用一个字节的存储空间 - 这是他们地址的字节。

  

可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。

在这种情况下,您可以使用空基类优化:允许基础子对象具有与其所属的完整对象相同的地址,因此可以占用没有存储空间因此,您可以将谓词作为(可能是私有的)基类而不是成员附加到类。处理比成员更加繁琐,但应该消除开销。

  

调用A()(value)时应该期望什么类型的开销?

与调用非成员函数相比,唯一的开销是传递额外的this参数。如果函数是内联的,那么这应该被消除(通常情况下,当调用不能访问任何成员变量的成员函数时)。

答案 3 :(得分:1)

已经有很好的答案可以回答主要问题。我想解决您表达的担忧:

  

但是在处理小型容器时,这个成本非常严重。考虑到可能的数据对齐,我们每个类最多可以获得16个字节而不需要vars(?!)。假设我们有一个自定义容器,通常会包含一些int值。然后考虑一系列这样的容器(大约有1000000个成员)。开销将是16 * 1000000!可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。

避免持有A

的费用

如果容器的所有实例都依赖于类型A,则无需在容器中保存A的实例。只需在需要时在堆栈上创建A实例,即可避免与A的非零大小相关联的开销。

无法避免持有A

的费用

如果预期A具有多态性,则可能会被迫在容器的每个实例中保持指向A的指针。对于这样的容器,每个容器的成本上升了指针的大小。基类A中是否存在任何成员变量对容器的大小没有影响。

sizeof A

的影响

在任何一种情况下,空类的大小都应该与容器的存储要求无关。