为了账号安全,请及时绑定邮箱和手机立即绑定

为什么C ++ 11的POD“标准布局”定义是这样?

为什么C ++ 11的POD“标准布局”定义是这样?

C++
largeQ 2019-12-16 13:12:50
我正在研究C ++ 11中新的,宽松的POD定义(第9.7节)标准布局类是这样的类:没有非标准布局类(或此类数组)或引用的非静态数据成员,没有虚拟函数(10.3)和虚拟基类(10.1),对所有非静态数据成员具有相同的访问控制(条款11),没有非标准布局的基类,在最派生的类中没有非静态数据成员,在一个非静态数据成员中最多没有一个基类,或者在非静态数据成员中没有基类,并且没有与第一个非静态数据成员相同类型的基类。我强调了令我惊讶的一点。如果我们允许数据成员具有不同的访问控制,那会出什么问题?如果第一个数据成员也是基类,怎么办?即struct Foo {};struct Good : Foo {int x; Foo y;};struct Bad  : Foo {Foo y; int x;};我承认这是一个奇怪的结构,但是为什么不应该Bad禁止它Good呢?最后,如果一个以上的构成类具有数据成员,那会出什么问题?
查看完整描述

3 回答

?
炎炎设计

TA贡献1808条经验 获得超4个赞

您可以将标准布局类对象地址强制转换为指向其第一个成员的指针,并通过后面的段落将其返回,这通常也可以在C语言中完成:


struct A { int x; };

A a;


// "px" is guaranteed to point to a.x

int *px = (int*) &a;


// guaranteed to point to a

A *pa = (A*)px; 

为此,第一个成员和complete对象必须具有相同的地址(编译器无法通过任何字节调整int指针,因为它无法知道它是否是int的成员A)。


最后,如果一个以上的构成类具有数据成员,那会出什么问题?


在一个类中,成员根据声明顺序以递增地址分配。但是,C ++并未规定跨类的数据成员的分配顺序。如果派生类和基类都具有数据成员,则Standard不会故意为其地址定义顺序,以便在内存布局方面为实现提供完全的灵活性。但是,要使上述演员表正常工作,您需要知道分配顺序中的“第一个”成员是什么!


如果第一个数据成员也是基类,怎么办?


如果基类与第一个数据成员具有相同的类型,则将基类放在内存中派生类对象之前的实现将需要在内存中派生类对象数据成员之前具有填充字节(基类的大小为1 ),以避免对基类和第一个数据成员使用相同的地址(在C ++中,相同类型的两个不同对象始终具有不同的地址)。但是,这将再次使得无法将派生类对象的地址转换为其第一个数据成员的类型。




查看完整回答
反对 回复 2019-12-17
?
HUWWW

TA贡献1874条经验 获得超12个赞

基本上是关于与C ++ 03和C的兼容性:

  • 相同的访问控制-允许C ++ 03实现使用访问控制说明符作为对类的(组)成员重新排序的机会,例如为了更好地打包它。

  • 层次结构中有多个具有非静态数据成员的类-C ++ 03没有说基类位于何处,也没有说明填充是否会存在于同类型的完整对象中的基类子对象中。

  • 基类和相同类型的第一个成员-由于第二条规则,如果基类类型用于数据成员,则它必须是空类。许多编译器确实实现了空基类优化,因此Andreas关于具有相同地址的子对象的说法是正确的。我不确定标准布局类的含义是什么,这意味着基类子对象具有与相同类型的第一个数据成员相同的地址是不好的,但是基类子对象何时具有与不同类型的第一个数据成员相同的地址。[编辑:这是因为相同类型的不同对象具有不同的地址,即使它们是空的子对象也是如此。感谢约翰内斯]

的C ++ 0x大概可以有定义的那些东西都是标准布局类型也是如此,在这种情况下,它也将确定他们是如何布局,以它为标准布局类型相同的程度。Johannes的回答更进一步,请看他的示例,这些示例会干扰标准布局类的良好属性。

但是,如果这样做,则某些实现将被迫更改它们对类的布局方式,以适应新的要求,这对于C ++ 0x前后的不同版本的编译器之间的结构兼容性造成了麻烦。基本上,它破坏了C ++ ABI。

我对如何定义标准布局的理解是,他们着眼于在不破坏现有实现的情况下可以放宽哪些POD要求。因此,我假设不做任何检查,以上是一些现有的C ++ 03实现确实使用类的非POD性质来执行与标准布局不兼容的示例。



查看完整回答
反对 回复 2019-12-17
?
www说

TA贡献1775条经验 获得超8个赞

如果我们允许数据成员具有不同的访问控制,那会出什么问题?


当前的语言表示,编译器无法在相同的访问控制下对成员重新排序。喜欢:


struct x

{

public:

    int x;

    int y;

private:

    int z;

};

这里x必须在y之前分配,但是相对于x和y对z没有限制。


struct y

{

public:

    int x;

public:

    int y;

};

新的措辞说,y尽管有两个publicS,但仍是POD 。这实际上是放宽规则


查看完整回答
反对 回复 2019-12-17
  • 3 回答
  • 0 关注
  • 452 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信