提问者:小点点

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


我发现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类型的两个成员。但在处理小型容器时,这样做的代价是相当严重的。考虑到可能的数据对齐,我们可以在没有var的情况下每个类最多获得16个字节 (?!). 假设我们有一个自定义容器,它通常会保存几个int值。然后考虑一个这样的容器数组(大约有1000000个成员)。开销将是16*1000000!可能发生的典型情况是容器类,其比较谓词存储在成员变量中。另外,考虑到类实例应该总是占用一些空间,那么调用A()(value)时应该期望什么类型的开销?


共3个答案

匿名用户

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

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

匿名用户

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

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

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

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

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

empty array[2];

这不再起作用,因为不允许在不同对象空[0]空[1]之间进行填充。

匿名用户

所有完整的对象都必须有一个唯一的地址;因此它们必须占用至少一个字节的存储空间——它们地址处的字节。

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

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

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

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

相关问题