非多态 C++ class 的 sizeof 如何大于其成员的 sizeof 之和?

How sizeof a not-polymorphic C++ class can be larger than the summed sizeof of its members?

在下面的例子中,struct E 继承了 struct CD 并且没有其他数据成员:

struct A{};
struct B{};
struct C : A, B {};
struct D : A, B {};
struct E : C, D {};

int main() {
    static_assert(sizeof(C) == 1);
    static_assert(sizeof(D) == 1);
    //static_assert(sizeof(E) == 2); // in Clang and GCC
    static_assert(sizeof(E) == 3); //in MSVC
}

在我测试过的所有编译器中,sizeof(C)==1sizeof(D)==1,并且仅在 MSVC sizeof(E)==3 中超过其 parents/members 的总大小,演示:https://gcc.godbolt.org/z/aEK7rjKcW

实际上我希望找到 sizeof(E) <= sizeof(C)+sizeof(D)(以防空碱基优化)。而且这里几乎没有任何填充,否则 sizeof(E) 将是 2 或 4。

E 中额外的 space (sizeof(E)-sizeof(C)-sizeof(D) == 1) 的目的是什么?

首先,由于填充和对齐,它可能大于子对象的总和。但是,您可能已经意识到这一点,而这不是您要问的。

要确定您的布局,您可以使用以下代码打印所有子对象的偏移量(及其类型的大小):

static E x;
int main() {
    E *e = &x;
    C *c = e;
    D *d = e;
    A *ca = c, *da = d;
    B *cb = c, *db = d;
#define OFF(p) printf(#p " %d %d\n",  (int)((char*)p - (char*)e), (int)sizeof(*p))
    OFF(e);
    OFF(c);
    OFF(ca);
    OFF(cb);
    OFF(d);
    OFF(da);
    OFF(db);
}

gcc/clang 的输出是:

e 0 2
c 0 1
ca 0 1
cb 0 1
d 1 1
da 1 1
db 1 1

MSVC 的输出是:

e 0 3
c 0 1
ca 0 1
cb 1 1
d 2 1
da 2 1
db 3 1

这表明MSVC实现EBO的方式与其他编译器不同。特别是,它没有将 AB 放在 C 内的同一地址和 D 内的同一地址(就像其他编译器所做的那样),而是将它们放在不同的偏移量。然后,即使sizeof(C) == 1,当它是一个子对象时,它也会为它分配完整的两个字节。这样做很可能是为了避免 cb 将其他一些 B 从另一个子对象中别名化,即使在这种情况下这不是问题。