我正在调查C++11中新的,轻松的POD定义(第9.7节)
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类
- 要么在大多数派生类中没有非静态数据成员,要么最多只有一个具有非静态数据成员的基类,或者没有带有非静态数据成员的基类,并且< / LI>
- 没有与第一个非静态数据成员相同类型的基类。
我突出了让我感到惊讶的一些事情。
如果我们容忍具有不同访问控制的数据成员会出现什么问题?
如果第一个数据成员也是基类,会出现什么问题?即。
struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad : Foo {Foo y; int x;};
我承认这是一个奇怪的结构,但为什么Bad
被禁止而不是Good
?
最后,如果不止一个组成类有数据成员会出现什么问题呢?
答案 0 :(得分:23)
它基本上与C ++ 03和C的兼容性:
C ++ 0x可能可以定义那些东西也是标准布局类型,在这种情况下它也会定义它们的布局方式,与标准布局类型相同 - 布局类型。 Johannes的答案进一步探讨了这一点,看看他的标准布局类的一个很好的属性的例子,这些事情会干扰。
但如果它这样做,那么一些实现将被迫改变它们如何布置类以匹配新需求,这对于C ++之前和之后该编译器的不同版本之间的结构兼容性是一种麻烦0X。它基本上打破了C ++ ABI。
我对如何定义标准布局的理解是,他们研究了在不破坏现有实现的情况下可以放宽哪些POD要求。所以我假设没有检查,以上是一些现有的C ++ 03实现 使用类的非POD特性做一些与标准布局不兼容的例子。
答案 1 :(得分:21)
您可以将标准布局类对象地址转换为指向其第一个成员的指针,然后通过后面的一个段落返回,这通常也在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;
为了使它工作,第一个成员和完整对象必须具有相同的地址(编译器不能通过任何字节调整int指针,因为它无法知道它是否是A
的成员或不)。
最后,如果不止一个组成类有数据成员会出现什么问题呢?
在一个类中,成员根据声明顺序分配增加的地址。但是,C ++并没有规定跨类的数据成员的分配顺序。如果派生类和基类都有数据成员,则标准不会故意定义其地址的顺序,以便为布局内存提供完全的灵活性。但是为了使上面的演员工作,您需要知道分配顺序中的“第一”成员是什么!
如果第一个数据成员也是基类,会出现什么问题?
如果基类与第一个数据成员具有相同的类型,那么将基类放在内存中的派生类对象之前的实现需要在内存中派生类对象数据成员之前有一个填充字节(基类会具有大小一),以避免基类和第一个数据成员具有相同的地址(在C ++中,相同类型的两个不同对象始终具有不同的地址)。但是,这也无法将派生类对象的地址转换为其第一个数据成员的类型。
答案 2 :(得分:7)
如果我们容忍具有不同访问控制的数据成员会出现什么问题?
当前语言表示编译器无法在同一访问控制下重新排序成员。像:
struct x
{
public:
int x;
int y;
private:
int z;
};
此处x必须在y之前分配,但z相对于x和y没有限制。
struct y
{
public:
int x;
public:
int y;
};
新措辞表明y
仍然是POD,尽管有两个public
。这实际上是对规则的放宽。
答案 3 :(得分:4)
至于为什么不允许Bad
让我从我发现的一篇文章中退出:
这确保了两个具有相同类类型的子对象 属于同一个最派生对象的属性不在 同一地址。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2172.html
答案 4 :(得分:2)
从子弹5开始,似乎两者都是非pod,因为大多数派生类都有非静态数据成员(int),它不能有一个带有非静态数据成员的基类。
我理解为:“只有一个”基类“(即类本身或它继承的类之一)可以拥有非静态数据成员”
答案 5 :(得分:1)
struct Good
也不是标准布局,因为Foo和Good具有非静态数据成员。
这样,Good应该是:
struct Foo {int foo;};
struct Good : public Foo {Foo y;};
未能满足第6个子弹。因此第6弹?