gpt4 book ai didi

Why does class C, which only contains member variables of type B, have sizeof(C) not equal to sizeof(B)?(为什么只包含类型B的成员变量的类C的sizeof(C)不等于sizeof(B)?)

转载 作者:bug小助手 更新时间:2023-10-25 17:11:55 25 4
gpt4 key购买 nike



I have written three classes: A, B, and C. A has no member variables, B has an integer type member variable, and C has a member variable of type B, as shown below:

我已经编写了三个类:A、B和C。A没有成员变量,B有整数类型的成员变量,C有B类型的成员变量,如下所示:


class A {
public:

};

class B : public A{
private:
int value_;
};

class C : public A{
private:
B a;
};

What surprises me is that sizeof(C) is 8 instead of 4, as expected like sizeof(B).
I had considered that this might be due to padding, but when I removed either C inheriting from A or B inheriting from A, sizeof(C) became 4 as expected.

令我惊讶的是,sizeof(C)是8,而不是像sizeof(B)那样预期的4。我曾考虑过这可能是由于填充,但当我删除继承自A的C或继承自A的B时,sizeof(C)如预期的那样变为4。


I observe the same behavior on a 64-bit machine when using both GCC and Clang.

在64位计算机上同时使用GCC和Clang时,我观察到了相同的行为。


I want to know why the compiler adds an additional 4 bytes of storage overhead when compiling C.

我想知道为什么编译器在编译C语言时会额外增加4个字节的存储开销。


更多回答

class A has a non-zero size (probably 1) due to: "every object must have a distinct address". So C is composed of A plus B plus padding.

A类的大小不是零(可能是1),这是因为:“每个对象都必须有不同的地址”。所以C是由A+B+填充组成的。

Is this the minimum sample? Does class A have virtual functions too? What is your build platform?

这是最小的样品吗?A类也有虚拟函数吗?您的构建平台是什么?

@Red.Wave It’s a minimum sample. The issue is that C has A as a superclass twice, via B. Otherwise you’d get sizeof(C) == sizeof(B).

@Red.Wave这是最小样本。问题是C有两次A作为超类,通过B。否则你会得到sizeof(C)==sizeof(B)。

@user17732522 was trying not to to go into that in a comment - sorry I missed that out.

@user17732522试图在评论中不涉及这一点-抱歉我错过了这一点。

@RichardCritten Sorry, I didn't read the question properly. There are two A objects here and they may not share an address. So in that sense it applies. Interesting that MSVC does not behave conforming.

@RichardCritten对不起,我没有正确阅读问题。这里有两个A对象,它们可能不共享地址。因此,从这个意义上说,它也适用。有趣的是,MSVC的行为并不符合。

优秀答案推荐

There are two A subobjects in C: The direct A base class subobject and the A base class subobject of the B member subobject.

C中有两个A子对象:直接的A基类子对象和B成员子对象的A基类子对象。


The standard requires that these two objects, because they have the same type and overlapping lifetime, and because one is not nested within the other, do not have the same address. (see [intro.object]/9)

该标准要求这两个对象不具有相同的地址,因为它们具有相同的类型和重叠的生存期,并且因为一个对象不嵌套在另一个对象中。(参见[Intro.Object]/9)


As a consequence it isn't possible to layout the class C so that both the direct A subobject and the B member subobject are allocated at offset 0.

因此,不可能对类C进行布局,以便直接A子对象和B成员子对象都分配在偏移量0处。


Assuming allocation starts in order with the direct A base class subobject at offset 0, then the best possible allocation strategy that also respects alignment requirements is to put the B member subobject at offset 4 which is the alignment requirement of both int and B. Consequently the total size will be at least 8.

假设分配是以偏移量0处的直接A基类子对象的顺序开始的,那么也尊重对齐要求的最佳可能的分配策略是将B成员子对象放置在偏移量4处,这是int和B的对齐要求。因此,总大小将至少为8。


Technically I think it would also be permitted by the standard (ignoring the issue raised at the end of this question) to layout the B member subobject at offset 0 and the A member subobject at any offset other than 0, because the A base class subobject is a subobject of zero size that may overlap with part of the int subobject. I think there is also no ordering requirement that enforces the base class subobject to have an address comparing smaller-or-equal to that of the B member subobject. With that choice the sizeof(C) == 4 would be possible. But that would be a surprising layout.

从技术上讲,我认为标准(忽略此问题末尾提出的问题)也会允许将B成员子对象和A成员子对象布置在偏移0处,因为A基类子对象是大小为零的子对象,可能与INT子对象的一部分重叠。我认为也没有顺序要求强制基类子对象的地址小于或等于B成员子对象的地址。有了这个选择,(C)==4的大小将是可能的。但这将是一个令人惊讶的布局。


So far about the requirements imposed on layout by the standard, but the actual specific layout that the compiler will choose is not specified by the standard. Instead it would be determined by the C++-ABI in use, which is the Itanium C++ ABI if you are compiling with GCC or Clang on a Linux system. The allocation algorithm used by this ABI is specified here (for classes that aren't POD for the purpose of layout, which C isn't). You can see the special case in there:

到目前为止,关于标准对布局的要求,但编译器将选择的实际具体布局并未由标准指定。相反,它将由正在使用的C++-ABI确定,如果您在LINUX系统上使用GCC或Clang进行编译,则该C++-ABI就是安腾C++ABI。这里指定了该ABI使用的分配算法(对于出于布局目的而不是POD的类,C不是)。你可以在那里看到特例:



Place D at this offset unless doing so would result in two components (direct or indirect) of the same type having the same offset. [...]



MSVC, following its own C++-ABI instead, says sizeof(C) == 4 and by my tests seems to give both the direct A base class subobject as well as the B member subobject offset 0, using a different allocation strategy that doesn't conform to the above requirements.

而MSVC,在它自己的C++-ABI之后,说sizeof(C)==4,根据我的测试,似乎既给出了直接的A基类子对象,也给出了B成员子对象的偏移量0,使用了不符合上述要求的不同的分配策略。




Unfortunately there is a defect in the standard that makes it impossible to correctly layout C.

不幸的是,标准中有一个缺陷,使得不可能正确地布局C。


On the one hand as shown above, B must be placed at an offset from the direct A base subobject, however according to the current definition in the standard C is a standard-layout class, which implies per [basic.compound]/4 that both the direct A base class subobject as well as the B member subobject as first non-static member of C must be placed at the same address as the C object, i.e. at offset 0.

一方面,如上所述,B必须被放置在与直接A基子对象的偏移量处,然而,根据标准C中的当前定义是标准布局类,这意味着按照[基本复合]/4,直接A基类子对象以及作为C的第一个非静态成员的B成员子对象必须被放置在与C对象相同的地址,即偏移量0。


This is a defect in the standard that (I guess) will probably be resolved by making it so that C is not standard-layout. Then MSCV's ABI would remain non-conforming, which however also might make adopting that resolution to the defect difficult.

这是标准中的一个缺陷,(我想)可能会通过使C不是标准布局来解决。那么MSCV的ABI将保持不合格,然而,这也可能使采用该解决方案来解决缺陷变得困难。


See open CWG issue 2736.

请参阅打开的CWG第2736期。



If there were no padding, then the following assertion would be true (ignoring that the class member is private, which is not relevant):

如果没有填充,则以下断言将为真(忽略类成员是私有的,这是不相关的):


C c;

A *a1=&c;
A *a2=&c.a;

static_assert(a1 == a2);

There are two distinct A objects, yet their pointers would be the same if there were no padding. This is un-possible.

有两个不同的A对象,但如果没有填充,它们的指针将是相同的。这是不可能的。


更多回答

"It is also required that the direct A base class subobject is allocated at offset 0, because C is a standard-layout class." I cannot find this requirement in the standard, can you provide a reference?

还需要在偏移量0处分配直接的A基类子对象,因为C是标准布局类。我在标准中找不到这个要求,你能提供一个参考吗?

@n.m.couldbeanAI The C object is pointer-interconvertible with the base class subobject because C is standard-layout. This implies same addresses, i.e. offset 0. See eel.is/c++draft/basic.compound#4 (specifically bullet 4.3)

@n.m.couldbeanAI C对象与基类子对象是指针可互换的,因为C是标准布局。这意味着相同的地址,即偏移量0。见eel.is/c++草稿/basic.化合物#4(特别是项目符号4.3)

@n.m.couldbeanAI See my edit.

@n.m.couldbeanI查看我的编辑。

@leihu [intro.object]/9

@Leihu[Intro.Object]/9

@BenVoigt Yes, and similarly the original C shouldn't be standard-layout either. But the rules in [class.prop]/3.7 do not exclude this situation.

@BenVoigt是的,同样地,原始的C也不应该是标准布局。但[Class.prop]/3.7中的规则并不排除这种情况。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com