导入
统一初始化使用 {}
在本节课中,我们将学习如何使用 {} 来初始化变量。
C++11 中变量的初始化方式变得统一。对于统一初始化,我们需要使用 {} 括号。
{}初始化始终适用。
直接初始化
变量可以不使用赋值运算符直接声明:
1std::string str{"my String"}; 2int i{2011};
拷贝初始化
{} 也支持使用 = 运算符进行拷贝初始化:
1std::string str = {"my String"}; 2int i = {2011};
两者的区别在于:直接初始化会直接调用类型的构造函数,而拷贝初始化则是先创建一个值,再隐式转换为目标类型。
防止截断
截断(更准确地说是截断转换)是指将数值从一种类型隐式转换为另一种类型。这可能导致精度丢失,可能非常危险。
下面的例子展示了传统方式初始化基本类型时可能出现的问题。
编译器会发出警告,但仍然执行隐式转换,导致数据丢失。
无论使用直接初始化还是赋值方式,问题都存在:
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#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}
现在,这个不符合标准的程序会被拒绝编译。
输出结果还取决于所使用的编译器。使用 GCC 6.1 及以上版本时,会报错;低于该版本的 GCC 仅会发出警告。
不信?可以使用在线编译器 https://gcc.godbolt.org/ 测试一下。
clang++ 编译器的行为更加一致可靠。因此这里给出一个简单建议:
编译程序时,让截断被视为错误。
可以通过添加编译选项 -Werror=narrowing,使得 GCC 4.8 及以上版本直接拒绝程序,而不是仅仅发出警告。
再看一个例子:
1#include <iostream> 2using namespace std; 3 4int main() { 5 char c1{97}; 6 char c2 = {97}; 7 std::cout << "c1: " << c1 << std::endl; 8 std::cout << "c2: " << c2 << std::endl; 9}
表达式 char c1{97} 不属于截断,因为 97 在 char 类型的取值范围内。同样,char c2 = {97} 也不属于截断。
让我们通过另一个例子来更深入理解 {} 初始化。