如果空基类影响派生类的布局
Should an empty base class affect the layout of the derived class?
c++标准(引自n3242草案)对子对象[intro.object]有如下规定:
除非对象是位域或基类的子对象为0大小,该对象的地址是它的第一个字节的地址占据了。两个不同的对象,既不是位域也不是基大小为0的类子对象必须有不同的地址。
现在,给定下面的代码片段:
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认为这个标准的意思是,如果一个子对象的大小为零,你就可以做任何你想做的事情。
这是未指定的行为吗?
扩展我之前的评论:
对象由其地址标识。如果比较两个相同类型对象的地址(如指针),如果比较结果相等,则认为这两个指针指向同一个对象。
不同类型的对象不能以这种方式直接比较,因此允许它们具有相同的地址。一个例子是结构体及其第一个成员。它们不能是同一类型的。基类和派生类也不能,所以如果基类为空,它们可能具有相同的地址。
但是,基类和派生类的第一个成员可以具有相同的类型。这不是问题,除非基类也是空的,并且编译器尝试空基类优化。在这种情况下,指向同一类型的两个不同对象的指针比较相等,因此可以认为它们是同一个对象。因此,如果成员具有不同的类型(empty和char),它们可以具有相同的地址。如果它们是相同的类型,它们不能,因为这会破坏对对象标识的测试,比如if (this != &that)
,有时用来测试像自我赋值这样的东西。
顺便说一下,微软同意这是他们编译器中的一个错误,但有其他更紧急的事情要先修复。
这取决于实现。
标准明确允许空基优化,但不要求它。事实上,该标准对内存中类的布局没有太多要求,只要求某些类彼此之间的布局兼容(但不要求通用的布局是什么)。还指定了成员的顺序(当没有中间的可访问性说明符时),但是允许填充、页眉、页脚和各种奇怪的东西。
在c++ 11标准的最终版本中,该段落被修改为:
除非对象是位域或基类的子对象为0大小,该对象的地址是它的第一个字节的地址占据了。两个不是位域的对象可以有相同的如果其中一个是另一个的子主语,或者至少有一个是大小为零的基类子对象,它们具有不同的类型;否则,它们必须有不同的地址。
虽然我不确定我理解这与对象的大小有什么关系
这篇文章有很好的解释。我只是想补充一下,为了解决这个结构膨胀的问题,你可以简单地使empty
类成为一个模板,这样用不同的模板参数实例化它就会使它成为一个不同的类:
template<class T>
struct empty { };
struct member: empty<member> { };
struct derived: empty<derived> { member m; };
int main(void)
{
printf("%dn", sizeof(derived));
return 0;
}
输出1
。
这就是在大型项目中避免使用boost::noncopyable
的原因。
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 在派生函数中指定void*参数
- 如何通过派生类函数更改基类中的向量
- 如何委托派生类使用其父构造函数?
- 如何使用C/C++在MacOSX中获得键盘布局
- 如何使用单独文件中的派生类访问友元函数对象
- 派生类销毁的最佳实践是什么
- 如何使用基类指针引用派生类成员
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 存储模板类型以强制转换回派生<T>
- 需要从 istream 和 ostream 派生 iostream
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- 在 C++ 中用派生类型重写成员函数
- 如何读取指向派生类的基类指针数组的内存布局
- Gcc /clang在基结构的后填充中布局派生结构的字段
- 如果空基类影响派生类的布局
- C++ POD 结构继承?是否有任何关于派生成员的内存布局的保证