8

我发现这个程序的编译器之间存在一些不一致,

struct A {
};

struct B : public A {
    float m;
};

struct C : public A {
    B b;
    float n;
};

struct D : public A {
    float n;
    B b;
};

static_assert(sizeof(A) == 1, "");
static_assert(sizeof(B) == 4, "");
static_assert(sizeof(C) == 8, ""); // most compilers say this is 12
static_assert(sizeof(D) == 8, "");

大多数编译器断言 sizeof(C) == 8 说 sizeof(C) 实际上是 12。我发现的唯一编译器没有并说它是 8 是 Microsoft Visual Studio 2010。

比我更聪明的人告诉我的原因是,在 B 中有两个单独的 A 引用需要保留彼此不同的单独偏移量。首先,从 C 派生的 A 在偏移量 0 处,并且成员 b 内的第二个 A 不能与第一个 A 在 0 处的偏移量相同,因此插入了 4 个字节的填充。

由于大多数编译器已经实现了这种行为,我想知道你需要什么情况来确保两个 A 有不同的引用?寻找一些关于为什么会这样的直觉?

有人说这可能是标准要求的条件,我们很好奇这是什么原因?

谢谢

4

3 回答 3

3

是的,这在 10p8 中提到:

基类子对象的大小可能为零(第 9 条);但是,具有相同类类型且属于相同的最派生对象的两个子对象不得分配在相同的地址 (5.10)

C你有两个As 中,一个是继承的,另一个是 s 的一部分B。微软在不应该的情况下积极而错误地在这里使用了空基类优化。我相信这是已知的错误,但在 Microsoft Connect 上很难找到错误报告。

于 2012-12-05T23:24:27.353 回答
3

该标准明确要求同一类型的每个对象的地址是不同的。相关条款是 5.10 [expr.eq] 第 1 段:

相同类型的两个指针比较相等当且仅当它们都为空,都指向同一个函数,或都表示相同的地址(3.9.2)。

这是区分这两个对象所必需的。对象既有值又有身份。对于基类子对象,与包含类具有相同地址是合理的。对于一个类的成员,你可以通过它们的类型来区分这两个对象的身份,即它们具有相同的地址是可以的。对于相同类型的两个对象,您仍然需要一些东西来区分对象的身份。

于 2012-12-05T23:31:21.237 回答
1

在 C++ 中,一个对象由一对数据唯一确定:它的地址和它的类型。

正如您正确地观察到的那样,两者都C包含D两个不同的子对象A,这必须是正确的。但是,根据您B在内存中的布局方式,您可以看到可能无法将两个A子对象放在 8 字节结构内的不同地址。

为什么不打印出子对象的实际数字地址?

于 2012-12-05T23:32:26.547 回答