考虑
struct base {};
struct child : base {};
众所周知,通过应用空基优化,sizeof(child)
可以为1。
然而,现在考虑
struct base {};
struct child : base {base b;};
编译器现在可以应用空基优化,还是sizeof(child)
必须至少为2?
答案 0 :(得分:18)
不,它不能。来自同一参考:
如果其中一个空基类,则禁止空基优化 也是第一个非静态数据类型的类型或基础 构件
因此sizeof(child) >= 2
。
答案 1 :(得分:15)
规则是相同类型的子对象不能在同一地址。这里有2个X
个子对象,因此每个子对象必须位于不同的地址。
相同类型的对象不能共享相同的地址,因为C ++中对象的标识是其地址。如果相同类型的多个对象共享相同的地址,则它们是无法区分的。这就是最小完整对象大小为1的原因,因此数组中的每个对象都有一个不同的地址。请参阅“§C++对象模型[intro.object]”:
对象是存储区域。
...
对象可以包含其他对象,称为子对象。子对象可以是成员子对象(9.2),基类子对象(子句10)或数组元素。不是任何其他对象的子对象的对象是 称为完整对象。
...
除非它是位字段(9.6),否则最派生的对象应具有非零大小并且应占用一个或多个存储字节。基类子对象的大小可以为零。
...
除非对象是零字段或零大小的基类子对象,否则该对象的地址是它占用的第一个字节的地址。如果一个是另一个的子对象,或者如果至少一个是零大小的基类子对象并且它们是不同类型的,则不是位字段的两个对象可以具有相同的地址;否则,他们应有不同的地址。
这就是为什么,例如,boost::noncopyable
可以增加一个类大小,如果一个人在不知不觉中通过空基类间接地继承它。例如。在:
struct A : boost::noncopyable {};
struct B : boost::noncopyable {};
struct C : boost::noncopyable {};
struct D : A, B, C {};
sizeof(D) == 3
因为有三个不同的boost::noncopyable
子主题。如果删除boost::noncopyable
的派生,则sizeof(D) == 1
。
答案 2 :(得分:14)
C ++中的对象需要具有唯一的“身份”。来自[intro.object] / 8(N4659):
两个对象
a
和b
具有重叠的生命周期而非位字段,如果一个嵌套在另一个中,则可能具有相同的地址,或者如果至少有一个是0的基类子对象尺寸和它们是不同类型的;否则,他们有不同的地址。
基类子对象和成员子对象是单独的对象;两者都没有“嵌套”在另一个之内。因此,如果它们属于同一类型,则它们必须具有单独的地址。
请注意,这会以递归方式扩展。请考虑以下事项:
struct eb1 {};
struct eb2 : eb1 {};
struct not_empty(eb1 a;};
struct derived : eb2 {not_empty b;};
由于C ++的唯一标识规则,derived::eb2::eb1
必须与derived::b::a
具有不同的地址。因此,编译器不能在derived
上使用EBO。
答案 3 :(得分:6)
我会在另一个更基本的引语中加入
如果一个嵌套在另一个中,则两个具有重叠生命周期且不是位字段的对象a和b可以具有相同的地址,或者如果至少一个是零大小的基类子对象并且它们具有不同类型,则它们可以具有相同的地址。否则,他们有不同的地址。
由于b
不是继承的base
的子对象,因此它们必须具有不同的地址。