默认初始化与零初始化
Posted
技术标签:
【中文标题】默认初始化与零初始化【英文标题】:Default Initialization Versus Zero Initialization 【发布时间】:2014-12-15 12:48:11 【问题描述】:我无法理解 gcc 4.8.1 或 Visual Studio 2015 在默认初始化与值初始化方面的行为。
我自己试图理解这些之间的差异并可能遇到编译器错误并没有帮助?
我的问题是:有人可以解释这种行为吗?最好告诉我应该发生什么。
我有两个班级:
class Foo
int _bar;
public:
void printBar() cout << _bar << endl;
;
class bar
int ent;
public:
int getEnt()return ent;
;
我正在使用以下代码进行测试:
int main()
Foo foo;
foo.printBar();
Foo().printBar();
bar b;
cout << b.getEnt() << endl;
return 0;
在 gcc 和 Visual Studio 上我得到:
134514795 0 0
现在如果我将测试代码更改为:
int main()
Foo foo;
foo.printBar();
bar b;
cout << b.getEnt() << endl;
return 0;
gcc 给我:
0 0
Visual Studio 给了我:
50790236 51005888
【问题讨论】:
如果有人感兴趣,我开始看这个以回应:***.com/a/27443703/2642059 这个问题不一样吗? @AntonSavin 我真的只想知道编译器应该对初始化做什么。我不认为这是同一个问题? 在调试版本中,通常使用填充符来帮助检测未初始化的值...而其他字节用于帮助检测其他一些与内存相关的问题(例如已释放的内存)。 【参考方案1】:像这样没有用户定义构造函数的类的默认初始化,什么都不做,给每个平凡的成员留下一个不确定的值。
值初始化将对每个成员进行零初始化。
在第一种情况下,您正在打印:
默认初始化Foo foo;
的不确定值
值初始化的零值Foo()
默认初始化bar b;
的不确定值
第三个恰好为零;可能是因为它重用了临时值初始化Foo
的存储。
在第二种情况下,您正在打印两个默认初始化对象的不确定值。巧合的是,它们在一种情况下具有零值,但在另一种情况下则没有。
两个程序都有未定义的行为,因为它们使用未初始化的值。
【讨论】:
【参考方案2】:Foo foo;
这个默认初始化foo
,由于Foo
的默认构造函数是微不足道的,它实际上根本没有初始化它,所以foo._bar
可以保存任何值(包括0).
Foo()
这个值初始化临时对象,在平凡的默认构造函数的情况下意味着零初始化,所以Foo()._bar
等于0。
【讨论】:
对不起,如果这有点迂腐,但你绝对不希望像struct String std::string str;;
这样的类因为它没有用户提供的默认构造函数而进行零初始化。【参考方案3】:
逻辑很简单:
-
类的默认初始化只是默认初始化所有成员。
内置类型的默认初始化使成员未初始化。
访问未初始化的对象会产生undefined behavior。
未定义的行为可以为所欲为。
两个编译器都提供“正确”的结果。请注意,导致发出 nasal demons 也是正确的。
【讨论】:
【参考方案4】:n3376 个引号
8.5/11
如果没有为对象指定初始化器,则该对象为 default-initialized; 如果不执行初始化,则对象为 自动或动态存储期限具有不确定的价值。 [ 笔记: 具有静态或线程存储持续时间的对象是零初始化的, 见 3.6.2。 ——尾注]
8.5/6
默认初始化 T 类型的对象意味着:如果 T 是一个(可能 cv-qualified) 类类型(第 9 条),T 的默认构造函数是 调用(如果 T 没有可访问的,则初始化格式错误 默认构造函数);
8.5/10
一个对象,其初始值设定项是一组空括号,即 (), 应该是值初始化的。
8.5/7
对 T 类型的对象进行值初始化意味着:
...
否则,对象被零初始化。
8.5/5
对 T 类型的对象或引用进行零初始化意味着:如果 T 是 (可能是 cv 限定的)非联合类类型,每个非静态数据 成员和每个基类子对象都是零初始化和填充 初始化为零位;
因此,在您的情况下,既没有静态存储持续时间变量,也没有线程局部变量,因此对象 foo
和 b
将被默认初始化,这意味着将调用该构造函数。默认构造函数(不是用户定义的)不会初始化成员,并且在成员中将是任意垃圾,这个任意垃圾可能是 0(感谢 Jarod42 在评论中指出这一点)。
而Foo().printBar();
应该打印 0,因为对象是零初始化的。
【讨论】:
任意垃圾可能是0
。
值得一提的是,Foo().printBar();
必须打印 0(至少单独打印,而不是在程序中有其他 UB 时)。
@Jarod42 这实际上是一个非常好的评论。我没有考虑过默认初始化可能会产生 0 结果。嗯……必须有办法处理这个问题,这样我才能真正看到发生了什么。以上是关于默认初始化与零初始化的主要内容,如果未能解决你的问题,请参考以下文章