该标准是否保证添加构造函数不会改变大小?

Does the standard make any guarantee that adding a constructor does not change the size?

假设我有一个 class

struct Foo {
    uint32_t x;
    uint32_t y;
};

如果 Bar 只是添加一个构造函数,C++ 标准是否提到 sizeof(Foo) 是否应该与 sizeof(Bar) 相同?

struct Bar {
    uint32_t x;
    uint32_t y;
    Bar(uint32_t a = 1,uint32_t b = 2) : x(a),y(b) {}
};

我问的原因是 Foo 作为 void* 通过网络发送,我不能改变它的大小,但如果可能的话我想添加一个构造函数。

我找到了一些相关的:here and here, but there the answers focus mainly on virtuals changing the size and I was looking for something more definite than "[...] all implementations I know of [...]"

PS:为避免误解...我不是在询问如何构建 Foo 然后将其作为 void* 发送,也不是在寻求解决方法确定大小不会改变,但我真的很好奇,在那种特定情况下,标准是否对 sizeof 有任何说明。

C++ 98 仅保证“普通旧数据”对象的布局,而那些不允许构造函数。

C++ 11 引入了“standard layout types”,它仍然保证布局,但确实允许添加构造函数和方法(并允许添加非虚拟基数,但空 类 除外)和重复)。

实际上,唯一影响布局的是对象中包含的数据——有一个重要的例外,稍后会谈到。如果你添加任何函数(实际上构造函数只不过是某种具有特殊语法的静态函数),你不会影响 class.

的布局

如果你有一个虚拟 class(一个至少有一个虚函数的函数,包括一个虚拟析构函数),你的 class 将包含一个 vtable 的条目(这不是由标准,但这是实现多态性的标准方式),但这只是指向其他地方某个特定内存位置的指针。如果您添加更多虚函数,vtable 本身将被修改,但不会对数据容器的布局产生任何影响(您的 class 实例实际上是)。

现在出现上面提到的异常:如果您将 virtual 函数添加到 class(或使现有函数成为虚拟函数,包括析构函数),而 class在之前没有任何虚函数(即没有自己的虚函数没有继承的!),然后 将新添加一个 vtable,然后 然后 数据布局 改变! (类似地,如果您将所有函数都设置为非虚函数(包括所有继承的函数),则 vtable 指针将被删除。

有标准保证吗?

编辑:

来自 C++ 标准,第 4.5 节 C++ 对象模型(§ 1):

[...] Note: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. — end note [...]

接下来是(我的)推导:一个函数(注意:不区分free或member one)不是对象,因此不能是子对象,因此不是对象数据的一部分。

进一步(同§):

An object has a type (6.9). Some objects are polymorphic (13.3); the implementation generates information associated with each such object that makes it possible to determine that object’s type during program execution.

(这就是 vtable!- 请注意,它并没有明确说明它们是如何实现的,甚至根本没有强制执行它们,如果编译器供应商找到了一些替代方案,则可以免费使用它...) .

For other objects, the interpretation of the values found therein is determined by the type of the expressions (Clause 8) used to access them.

好吧,找不到任何提示(到目前为止),功能是否或如何影响 class 的布局,而不是整个标准,而不是(特别出席)章节8(如上所述)或 12 "Classes"(尤其是 12.2 "Class members")。

似乎没有明确指定(不过,不会因为没有监督而将我的手投入火中......)。也许已经从不仅仅是对象的函数中推断出这一点是有效的...

标准布局 classes,正如 Jan Husec 所引用的那样,为布局提供了进一步的保证,例如不重新排序成员(允许不同可访问性的成员)、对齐条件,...

从成为 SLC 的那些条件,我推断至少对于这些,保证适用,因为布局兼容所引用的只是数据成员,没有提及(非静态成员)函数(除了不允许虚拟的...)。

正如其他答案所解释的那样,如果添加了虚拟析构函数,布局可能会发生变化。

如果您的析构函数不是虚拟的,您可以继续添加。但是如果你需要一个虚拟析构函数,你可以将结构包装在另一个结构中并将析构函数放在那里。

确保包装器可以访问您要修改的字段class。朋友关系应该够好了。

尽管您的所有继承都将通过包装器进行。内部结构只是为了保持布局,以便您可以通过网络发送它。