默认构造函数是否总是初始化所有成员? [英] Does a default constructor always initialize all members?

查看:177
本文介绍了默认构造函数是否总是初始化所有成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发誓我不记得以前看过这个,而且我难以相信自己的眼睛:

I could swear I don't remember having seen this before, and I'm having trouble believing my eyes:

为a做一个隐式定义的默认构造函数非聚集类是否初始化其成员?

Does an implicitly-defined default constructor for a non-aggregate class initialize its members or no?

在Visual C ++中,当我运行此看起来无害的代码时...

In Visual C++, when I run this innocent-looking code...

#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }

...令我惊讶的是,它返回的非零值!但是,如果删除字段 b ,则它返回零。

... to my astonishment, it returns a non-zero value! But if I remove field b, then it returns zero.

我已经在所有VC ++版本上尝试过此操作我可以动手,而且似乎可以对所有这些进行操作。

I've tried this on all versions of VC++ I can get my hands on, and it seems to do this on all of them.

但是,当我在Clang和GCC上尝试使用时,值初始化为零,不管我是在C ++ 98模式还是C ++ 11模式下尝试。

But when I try it on Clang and GCC, the values are initialized to zero, whether I try it in C++98 mode or C++11 mode.

正确的行为是什么?

推荐答案

引用C ++ 11:


5.2.3显式类型转换(功能符号)[expr.type.conv]

2表达式 T(),其中 T 简单类型说明符类型名称说明符对于非数组完整对象类型或(可能具有cv资格的) void 类型,将创建指定类型的prvalue,并对其进行值初始化(8.5; void()的情况下不会进行初始化)。 [...]

2 The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type,which is value-initialized (8.5; no initialization is done for the void() case). [...]

8.5初始化程序[dcl.init]

7要 value-initialize T 类型的对象的意思是:

7 To value-initialize an object of type T means:


  • ...

  • 如果 T 是(可能具有cv限定)非工会类类型,并且没有用户提供的构造函数,则该对象将初始化为零,并且 T 的隐式声明的默认构造函数很简单,即调用该构造函数。

  • ...

  • ...
  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is called.
  • ...

因此在C ++ 11中, S()。a 应该为零:在调用构造函数之前,该对象已初始化为零,并且构造函数不会将 a 的值更改为其他任何值。

So in C++11, S().a should be zero: the object is zero-initialized before the constructor gets called, and the constructor never changes the value of a to anything else.

在C ++ 11之前,值初始化有不同的描述。引用N1577(大致为C ++ 03):

Prior to C++11, value initialization had a different description. Quoting N1577 (roughly C++03):


要对值T的对象进行值初始化的意思是:


  • ...

  • 如果 T 是非工会类类型如果没有用户声明的构造函数,则 T 的每个非静态数据成员和基类组件都将进行值初始化;

  • ...

  • 否则,对象将初始化为零

  • ...
  • if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
  • ...
  • otherwise, the object is zero-initialized

此处, S 的值初始化未调用任何构造函数,但导致其 a b 个成员。然后,该 a 成员的值初始化导致该特定成员的初始化为零。在C ++ 03中,结果也保证为零。

Here, value initialization of S did not call any constructor, but caused value initialization of its a and b members. Value initialization of that a member, then, caused zero initialization of that specific member. In C++03, the result was also guaranteed to be zero.

甚至早于此,使用第一个标准C ++ 98:

Even earlier than that, going to the very first standard, C++98:


表达式 T(),其中 T 对于非数组完整对象类型或(可能经过cv限定) void 类型的simple-type-specifier (7.1.5.2),会创建一个右值指定的类型,其值由默认初始化确定(8.5; void()情况不进行初始化)。

The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, whose value is determined by default-initialization (8.5; no initialization is done for the void() case).


默认初始化类型为<的对象code> T 的意思是:


  • 如果 T 是非POD类类型(第9节),将调用 T 的默认构造函数(如果 T 没有可访问的默认构造函数);

  • ...

  • 否则,该对象的存储将初始化为零。

  • if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • ...
  • otherwise, the storage for the object is zero-initialized.

因此基于第一个标准,VC ++是正确的:当您添加 std :: string 成员时, S 变为非POD类型,并且非POD类型不会初始化为零,它们只是调用了其构造函数。为 S 隐式生成的默认构造函数不会初始化 a 成员。

So based on that very first standard, VC++ is correct: when you add a std::string member, S becomes a non-POD type, and non-POD types don't get zero initialization, they just have their constructor called. The implicitly generated default constructor for S does not initialise the a member.

因此可以说所有编译器都是正确的,只是遵循该标准的不同版本。

So all compilers can be said to be correct, just following different versions of the standard.

正如@Columbo在评论中所报告的那样,更高版本的VC ++确实会导致 a 成员要初始化,以符合C ++标准的最新版本。

As reported by @Columbo in the comments, later versions of VC++ do cause the a member to be initialized, in accordance with more recent versions of the C++ standard.

这篇关于默认构造函数是否总是初始化所有成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆