空基类是否应该影响派生类的布局?

时间:2011-10-13 20:43:24

标签: c++ standards unspecified-behavior

C ++标准(引自草案n3242)说明以下关于子对象[intro.object]:

  

除非对象是位字段或基类子对象为零   size,该对象的地址是它的第一个字节的地址   占据。两个不同的对象,既不是位字段也不是基础   零大小的类子对象应具有不同的地址。

现在,给出以下代码段:

struct empty { };
struct member: empty { };
struct derived: empty { member m; };

int main(void)
{
    printf("%d", sizeof(derived));
    return 0;
}

gcc我相信打印出2,而Visual C ++ 2010打印出1。我怀疑gcc正在采用标准来表示如果它们代表不同的对象,则不能对类型的存储进行别名。我打赌MSVC正在采用标准来表示如果一个子对象是零大小,你可以做任何你想做的事。

这是未指明的行为吗?

4 个答案:

答案 0 :(得分:5)

扩展我之前的评论:

对象由其地址标识。如果比较两个相同类型对象的地址(如指针),并且它们比较相等,那么指针就要指向同一个对象。

不能以这种方式直接比较不同类型的对象,因此允许它们具有相同的地址。一个例子是结构及其第一个成员。它们不能是同一类型。基类和派生类都不能,因此如果基类为空,它们可能具有相同的地址。

但是,基类和派生类的第一个成员可以属于同一类型。除非基类也为空并且编译器尝试空基类优化,否则这不是问题。在这种情况下,我们可以指向相同类型的两个不同对象比较相等,因此相信它们是相同的对象。

因此,如果成员具有不同的类型(空和char),则它们可以具有相同的地址。如果它们属于同一类型,则不能,因为这会破坏对象标识的测试,如if (this != &that),有时用于测试自我分配等事情。

顺便说一句,微软同意这是他们的编译器中的一个错误,但还有其他更紧急的事情需要先解决。

答案 1 :(得分:4)

这与实现有关。

标准明确允许空基优化,但不需要它。事实上,该标准并不需要关于内存中类的布局的任何内容,只是某些类将彼此布局兼容(但不是共同布局)。还指定了成员顺序(当没有中间可访问性说明符时),但允许填充,页眉,页脚和各种更奇怪的东西。

答案 2 :(得分:3)

在C ++ 11标准的最终版本中,该段修改为:

  

除非对象是位字段或基类子对象为零   size,该对象的地址是它的第一个字节的地址   占据。两个不是位字段的对象可以具有相同的   如果一个是另一个的子对象或者如果至少一个是a,则寻址   零大小的基类子对象,它们是不同类型的;   否则,他们应有不同的地址。

虽然我不确定我是否理解这与物体的大小有什么关系。

答案 3 :(得分:1)

这个帖子有很好的解释。我只是想添加它来解决这个结构膨胀问题,你可以简单地使empty类成为一个模板,这样用不同的模板参数实例化它就会使它成为一个不同的类:

template<class T>
struct empty { };
struct member: empty<member> { };
struct derived: empty<derived> { member m; };

int main(void)
{
    printf("%d\n", sizeof(derived));
    return 0;
}

输出1

这是避免在大型项目中使用boost::noncopyable的原因。