对相同基类的引用在内存中必须具有单独的偏移量



我发现编译器与该程序之间存在一些不一致,

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的编译器是Visual Studio 2010 Microsoft。

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

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

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

谢谢

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

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

这是

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

是的,这在 10p8 中提到:

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

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

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

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

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

最新更新