简体   繁体   English

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

[英]Why do C++ classes without member variables occupy space?

I found that both MSVC and GCC compilers allocate at least one byte per each class instance even if the class is a predicate with no member variables (or with just static member variables). 我发现MSVC和GCC编译器每个类实例至少分配一个字节,即使该类是没有成员变量的谓词(或只有静态成员变量)。 The following code illustrates the point. 以下代码说明了这一点。

#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;
}

Output: 输出:

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

My question is why does compiler need it? 我的问题是为什么编译器需要它? The only reason that I can come up with is ensure that all member var pointers differ so we can distinguish between two members of type A or B by comparing pointers to them. 我能提出的唯一原因是确保所有成员var指针不同,这样我们就可以通过比较它们的指针来区分A或B类型的两个成员。 But the cost of this is quite severe when dealing with small-size containers. 但是在处理小型容器时,这个成本非常严重。 Considering possible data alignment, we can get up to 16 bytes per class without vars (?!). 考虑到可能的数据对齐,我们每个类最多可以获得16个字节而不需要vars(?!)。 Suppose we have a custom container that will typically hold a few int values. 假设我们有一个自定义容器,通常会包含一些int值。 Then consider an array of such containers (with about 1000000 members). 然后考虑一系列这样的容器(大约有1000000个成员)。 The overhead will be 16*1000000! 开销将是16 * 1000000! A typical case where it can happen is a container class with a comparison predicate stored in a member variable. 可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。 Also, considering that a class instance should always occupy some space, what type of overhead should be expected when calling A()(value) ? 另外,考虑到类实例应该总是占用一些空间,在调用A()(value)时应该期望什么类型的开销?

It's necessary to satisfy an invariant from the C++ standard: every C++ object of the same type needs to have a unique address to be identifiable. 必须满足C ++标准中的不变量:相同类型的每个C ++对象都需要具有可识别的唯一地址。

If objects took up no space, then items in an array would share the same address. 如果对象没有占用空间,则数组中的项将共享相同的地址。

Basically, it's an interplay between two requirements: 基本上,它是两个要求之间的相互作用:

  • Two different objects of the same type must be at a different addresses. 相同类型的两个不同对象必须位于不同的地址。
  • In arrays, there may not be any padding between objects. 在数组中,对象之间可能没有任何填充。

Note that the first condition alone does not require non-zero size: Given 请注意,仅第一个条件不需要非零大小:给定

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

the the first requirement could easily be met by having a zero-size a followed by a single padding byte to enforce a different address, followed by a zero-size b . 通过使用零大小a后跟单个填充字节来强制执行不同的地址,然后是零大小b ,可以轻松满足第一个要求。 However, given 但是,给定

empty array[2];

that no longer works because a padding between the different objects empty[0] and empty[1] would not be allowed. 这不再有效,因为不允许在不同对象之间填充empty[0]empty[1]

All complete objects must have a unique address; 所有完整对象必须具有唯一的地址; so they must take up at least one byte of storage - the byte at their address. 所以它们必须占用至少一个字节的存储空间 - 它们的地址为字节。

A typical case where it can happen is a container class with a comparison predicate stored in a member variable. 可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。

In this case, you can use the empty base class optimisation: a base subobject is allowed to have the same address as the complete object that it's part of, so can take up no storage. 在这种情况下,您可以使用空基类优化:允许基础子对象具有与其所属的完整对象相同的地址,因此不占用任何存储空间。 So you can attach the predicate to a class as a (perhaps private) base class rather than a member. 因此,您可以将谓词作为(可能是私有的)基类而不是成员附加到类。 It's a bit more fiddly to deal with than a member, but should eliminate the overhead. 处理比成员更加繁琐,但应该消除开销。

what type of overhead should be expected when calling A()(value) ? 调用A()(value)时应该期望什么类型的开销?

The only overhead compared to calling a non-member function will be passing the extra this argument. 与调用非成员函数相比,唯一的开销是传递额外的this参数。 If the function is inlined, then this should be eliminated (as would be the case, in general, when calling a member function that doesn't access any member variables). 如果函数是内联的,则应该消除(通常情况下,调用不访问任何成员变量的成员函数时)。

There are already excellent answers that answer the main question. 已经有很好的答案回答了主要问题。 I would like to address the concerns you expressed with: 我想解决您表达的担忧:

But the cost of this is quite severe when dealing with small-size containers. 但是在处理小型容器时,这个成本非常严重。 Considering possible data alignment, we can get up to 16 bytes per class without vars (?!). 考虑到可能的数据对齐,我们每个类最多可以获得16个字节而不需要vars(?!)。 Suppose we have a custom container that will typically hold a few int values. 假设我们有一个自定义容器,通常会包含一些int值。 Then consider an array of such containers (with about 1000000 members). 然后考虑一系列这样的容器(大约有1000000个成员)。 The overhead will be 16*1000000! 开销将是16 * 1000000! A typical case where it can happen is a container class with a comparison predicate stored in a member variable. 可能发生的典型情况是容器类,其中比较谓词存储在成员变量中。

Avoiding the cost of holding A 避免持有A的成本

If all instances of a container depend on type A , then there is no need to hold instances of A in the container. 如果容器的所有实例都依赖于类型A ,则不需要在容器中保存A实例。 The overhead associated with the non-zero size of A can be avoided by simply creating an instance of A on the stack when needed. 通过在需要时在堆栈上简单地创建A的实例,可以避免与非零大小的A相关联的开销。

Not being able to avoid the cost of holding A 无法避免持有A的成本

You may be forced to hold a pointer to A in each instance of the container if A is expected to by polymorphic. 如果预期A具有多态性,则可能会强制在容器的每个实例中保持指向A的指针。 For such a containerthe cost of each container goes up by the size of a pointer. 对于这样的容器,每个容器的成本上升了指针的大小。 Whether there are any member variables in the base class A or not makes no difference to the size of the container. 基类A是否存在任何成员变量对容器的大小没有影响。

Impact of sizeof A sizeof A影响

In either case, the size of an empty class should have no bearing on the storage requirements of the container. 在任何一种情况下,空类的大小都应该与容器的存储要求无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM