空派生优化
Empty derived optimization
大多数C++程序员都知道空基类优化是一种技术/习惯用法空的子类会发生什么例如
class EmptyBase {
int i;
};
template<typename T>
class Derived : T {
};
std::cout << sizeof(Derived<EmptyBase>); // Is there a standard verdic on this?
类似于EBO,应该有一个EDO声明,因为派生类不提供任何更多的成员,也不向其参数化类型引入任何虚拟成员,所以它不应该需要更多的内存。考虑到可能出现类似情况的各种情况(多重继承、单一继承…):
- 这样的优化标准/可能吗
- 如果是,这种优化的机制是什么,它们与EBO的类似吗
注意:使用从其参数化类型派生的类模板是相当典型的。主题是关于这种情况下的空间浪费
标准本身不包含"空基类"情况。相反,它说(参见1.8):
[A]大多数派生对象应具有非零大小,并应占用一个或多个字节的存储。基类子对象的大小可能为零。
和:
除非对象是[…]大小为零的基类子对象,否则该对象的地址是它所占用的第一个字节的地址。如果一个对象是另一个对象的子对象,或者至少有一个是零大小的基类子对象并且它们具有不同的类型,则两个对象[…]可能具有相同的地址;否则,它们应具有不同的地址。
和(第9条):
类类型的完整对象和成员子对象的大小应为非零。脚注:基类子对象不受此约束。
这从来没有说只有空基地才适合任何形式的布局更改,并为"挤压"布局留下了充足的空间:例如:
struct A {}; struct B : A { int x; }; // "equivalent" to { int }
struct X { int a; }; struct Y : X {}; // "equivalent" to { int }
考虑B b;
和Y y;
。b
的地址、b
的A
-子对象(即&static_cast<A&>(b)
)的地址和b.x
的地址可能相同,并且类似地,y
的地址、y
的X
-子对象和y.a
的地址相同。
唯一不起作用的是:
struct S {}; struct T { S s; }; // "equivalent" to { char }
我所说的"equivalent"
是指最明智、节省空间的实现。
一个更有趣的案例如下:
struct Foo { int x; char a; }; // { int, char, char[3] }
struct Bar : Foo { short q; }; // { int, char, char, short }
本例假定sizeof(int) == 4
和sizeof(short) == 2
。由于对齐原因,我们有sizeof(Foo) == 8
,但sizeof(Bar)
也是8
,尽管有更多的数据成员。
标准的另一个相关部分是9.2/13:
具有相同访问控制(第11条)的(非并集)类的非静态数据成员被分配,以便稍后的成员在类对象中具有更高的地址。具有不同访问控制的非静态数据成员的分配顺序未指定(11)。实施一致性要求可能会导致两个相邻成员不能立即相互分配;管理虚拟功能(10.3)和虚拟基类(10.1)的空间需求也可能如此
最后,9.2/10说标准布局类在开始时没有填充,因此它们的地址等于"初始成员"的地址。由于标准布局要求所有基都是空的,或者派生类本身没有数据成员,这意味着标准布局类必须采用一种"空基"优化,而我上面的B
和Y
布局的初始部分实际上是强制性的。
- 为什么使用 "this" 指针调用派生成员函数?
- 空基优化子对象的地址
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 在派生函数中指定void*参数
- 如何通过派生类函数更改基类中的向量
- 如何委托派生类使用其父构造函数?
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 如何使用单独文件中的派生类访问友元函数对象
- 派生类销毁的最佳实践是什么
- 如何使用基类指针引用派生类成员
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 存储模板类型以强制转换回派生<T>
- 需要从 istream 和 ostream 派生 iostream
- 在 C++ 中用派生类型重写成员函数
- 如果所有派生类在编译时都是已知的,那么final关键字是否提供了优化
- 空派生优化
- 派生类中虚函数调用的优化