注意了!C++默认初始化与值初始化的这个细节,千万不要忽视!
在C++语言中,默认初始化和值初始化这两种变量初始化方式都可以调用类型的默认构造函数。它们的形式非常相似,大部分情况下作用相同,但它们之间存在容易忽视的微小差别。
值初始化的形式为:
T()
new T()
Class::Class(...) : member() {...}
T object{};
T{}
new T{}
Class::Class(...) : member{} {...}
默认初始化的形式为:
T object;
new T
从形式上看,值初始化与默认初始化的唯一区别是值初始化多了一对括号。在很多场合中,这两种初始化并不会产生什么能被观察到的区别。那么,下面这两段代码,结果有什么区别呢?
int a;
cout << a << endl;
int a{};
cout << a << endl;
在Visual Studio 2019中,第一段代码产生错误C4700(使用了未初始化的局部变量)且无法编译;关闭SDL检查之后可以编译,但运行时会被Runtime check中断。而第二段代码会正常运行并输出0。
参照cppreference.com中的介绍,进行值初始化时:
若T是类类型,
默认构造函数非用户提供且未被删除,则会进行零初始化
否则进行默认初始化
若T是数组类型,则会对数组的每个成员进行零初始化
若T既不是类类型也不是数组类型,则对T进行零初始化
而进行默认初始化时:
若T是类类型,则按照重载规则调用构造函数
若T是数组类型,则对每个元素进行默认初始化
否则,不做任何事(变量处于未初始化状态)
所以,对于数值类型,使用默认初始化就等于没有初始化。对于没有定义构造函数或是构造函数没有进行必要的初始化,且成员变量也没有定义初始值的类类型也同样如此。使用没有初始化的变量是非常危险的,不但会导致意外的结果,而且因为初始值的不确定性导致难以调试。编写代码时应确保变量在使用前被正确、有效地初始化,而不是被(象征性地)默认初始化。