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

时间:2017-04-07 10:08:53

标签: c++ constructor sizeof

让我说我有一个班级

struct Foo {
    uint32_t x;
    uint32_t y;
};

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

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

我之所以要问的是Foo是作为void*通过网络发送的,我无法更改其大小,但如果可能的话,我想添加一个构造函数。

我发现了一些相关内容:herehere,但答案主要集中在虚拟内容上,而且我正在寻找比"[...] all implementations I know of [...]"更明确的内容。

PS:为了避免误解......我不是问如何使用Foo来发送它,然后将其作为void*发送,我也不会要求解决方法以确保尺寸不会改变,但我真的很好奇,标准是否在该特定案例中说明sizeof

3 个答案:

答案 0 :(得分:4)

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

C ++ 11引入了“this issue”,它仍然保证布局,但允许添加构造函数和方法(并允许添加非虚拟基础,但空类和重复项有一些例外)。

答案 1 :(得分:1)

实际上,影响布局的唯一因素是对象中包含的数据 - 有一个重要的例外,稍后会出现。如果添加任何函数(实际上构造函数只是某些静态函数,只有特殊语法),则不会影响类的布局。

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

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

标准保证?

编辑:

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

  

[...]注意:函数不是对象,无论它是否以对象的方式占用存储。 - 结尾注释[...]

接下来是演绎(我的):一个函数(注意:如果空闲或成员一个没有区分)不是一个对象,因此不能是一个子对象,因此不是一个对象数据的一部分。

进一步(同样§):

  

对象具有类型(6.9)。有些对象是多态的(13.3);该实现生成与每个这样的对象相关的信息,这使得在程序执行期间可以确定该对象的类型。

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

  

对于其他对象,其中发现的值的解释取决于用于访问它们的表达式(第8条)的类型。

好吧,找不到任何提示(到目前为止),函数是否或如何影响一个类的布局,而不是作为一个整体超越标准,而不是(有特殊出勤)在第8章(如上所述)或12“班级”(特别是12.2“班级成员”)。

似乎没有明确规定(虽然......但不会因为没有监督而将我的手放进火中)。也许从函数中推断出这一点并不仅仅是对象......

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

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

答案 2 :(得分:0)

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

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

确保包装类可以访问要修改的字段。朋友关系应该足够好。

虽然你的所有继承都是通过包装器完成的。内部结构只是为了维护布局,以便您可以通过网络发送它。