为什么没有成员variables的C ++类占用空间?
我发现MSVC和GCC编译器为每个类实例分配至less一个字节,即使这个类是一个没有成员variables的谓词(或者只有静态成员variables)。 以下代码说明了这一点。
#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或Btypes的两个成员。 但是在处理小尺寸的容器时,这个代价是相当严重的。 考虑到可能的数据alignment,我们可以得到每个类没有variables(?!)的16个字节。 假设我们有一个自定义的容器,通常会保存几个int值。 然后考虑一个这样的容器数组(大约有1000000个成员)。 开销将是16 * 1000000! 一个可能发生的典型情况是一个存储在成员variables中的比较谓词的容器类。 另外,考虑到一个类实例应该总是占用一定的空间,当调用A()(value)时,应该期望什么types的开销?
必须满足C ++标准中的一个不variables:每个相同types的C ++对象都需要有一个唯一的地址可以被识别。
如果对象不占用空间,则数组中的项目将共享相同的地址。
基本上,这是两个要求之间的相互作用:
- 同一types的两个不同的对象必须位于不同的地址。
- 在数组中,对象之间可能没有任何填充。
请注意,单独的第一个条件不需要非零大小:给定
struct empty {}; struct foo { empty a, b; };
第一个要求可以很容易地通过一个零大小的a
后跟一个填充字节来强制执行一个不同的地址,然后是一个零大小的b
。 但是,给出
empty array[2];
不再有效,因为不同的对象empty[0]
和empty[1]
之间的填充是不允许的。
所有完整的对象必须有一个唯一的地址; 所以他们必须占用至less一个字节的存储空间 – 在他们地址的字节。
一个可能发生的典型情况是一个存储在成员variables中的比较谓词的容器类。
在这种情况下,您可以使用空基类优化:一个基础子对象被允许具有与其所属的完整对象相同的地址,因此可以不占用存储空间。 所以你可以把谓词附加到一个类(或许是私有的),而不是一个成员。 与会员打交道比较麻烦,但是应该消除这个开销。
调用A()(value)时应该预期什么types的开销?
与调用非成员函数相比,唯一的开销是传递额外的this
参数。 如果函数是内联的,那么这应该被消除(一般来说,当调用一个不访问任何成员variables的成员函数时就是这种情况)。
已经有很好的答案回答了主要问题。 我想解决你expression的关切:
但是在处理小尺寸的容器时,这个代价是相当严重的。 考虑到可能的数据alignment,我们可以得到每个类没有variables(?!)的16个字节。 假设我们有一个自定义的容器,通常会保存几个int值。 然后考虑一个这样的容器数组(大约有1000000个成员)。 开销将是16 * 1000000! 一个可能发生的典型情况是一个存储在成员variables中的比较谓词的容器类。
避免持有A
的成本
如果容器的所有实例都依赖于typesA
,则不需要在容器中保存A
实例。 与A
的非零大小关联的开销可以通过简单地在需要时在堆栈上创buildA
实例来避免。
无法避免持有A
的成本
如果A
被预期为多态,你可能会被迫在容器的每个实例中持有一个指向A
的指针。 对于这样的容器,每个容器的成本上升了一个指针的大小。 基类A
是否有成员variables对容器的大小没有影响。
sizeof A
的sizeof A
影响
在任何一种情况下,空的类别的大小都不应该影响容器的存储要求。