导入
使用 {} 的统一初始化
在本节课中,我们将学习使用 {} 进行初始化及其如何防止类型缩小。
C++11中变量的初始化方式统一。
初始化的两种形式
规则: 使用花括号
{}的初始化始终适用。
直接初始化
1string str{"my String"};
拷贝初始化
1string str = {"my String"};
防止窄化转换
使用 {} 的初始化禁止窄化转换。
窄化,更准确地说是窄化转换,是指算术值的隐式转换。这种转换可能导致结果精度降低,存在极大的安全隐患。
下面的例子展示了传统初始化在基本类型上的问题。无论使用直接初始化还是赋值,都会出现相同的问题。
1#include <iostream> 2 3int main(){ 4 char c1(999); 5 char c2 = 999; 6 std::cout << "c1: " << c1 << std::endl; 7 std::cout << "c2: " << c2 << std::endl; 8 9 int i1(3.14); 10 int i2 = 3.14; 11 std::cout << "i1: " << i1 << std::endl; 12 std::cout << "i2: " << i2 << std::endl; 13}
程序输出显示代码中有两个警告:第一,整型字面量 999 超出了 char 类型的表示范围;第二,双精度浮点字面量 3.14 无法完整表示为 int 类型。
而使用 {} 初始化则不会发生这种情况。
1// narrowingSolved.cpp 2 3#include <iostream> 4 5int main(){ 6 char c1{999}; 7 char c2 = {999}; 8 std::cout << "c1: " << c1 << std::endl; 9 std::cout << "c2: " << c2 << std::endl; 10 11 int i1{3.14}; 12 int i2 = {3.14}; 13 std::cout << "i1: " << i1 << std::endl; 14 std::cout << "i2: " << i2 << std::endl; 15 16 char c3{8}; 17 std::cout << "c3: " << c3 << std::endl; 18}
在不同版本的 GCC 编译器下可能会产生混淆。编译器版本的不同会影响结果:使用 GCC 6.1 及以上版本时会报错;而低于 GCC 6.1 的版本则仅给出警告。为了在低版本 GCC 中将警告升级为错误,可以使用 -Werror=narrowing 标志,这样程序就会触发错误。你可以在 这个编译器 上尝试验证。
相比之下,clang++ 编译器的行为更加一致和可预测。
确保你的程序在发生窄化时被当作错误处理。
再看一个例子:
直接初始化
1#include <iostream> 2using namespace std; 3 4int main() { 5 char c3{97}; 6 std::cout << "c3: " << c3 << std::endl; 7}
拷贝初始化
1#include <iostream> 2using namespace std; 3 4int main() { 5 char c3 = {97}; 6 std::cout << "c3: " << c3 << std::endl; 7}
在上述代码中,如果写成 char c3{8},这并不会造成窄化,因为数字 8 完全可以放入 char 类型。同样的情况也适用于 char c3 = {8}。
接下来的课程中,我们将通过几个示例进一步深入理解这一技术。