C++面试准备 初始化表达式{}与()的区别
在C++中,初始化一个变量的方式有很多种,如下:
int x(0); // "()"类型的初始化 int y = 0; // "="类型的初始化 int z{ 0 }; // "{}"类型的初始化,C++ 11中引入的
其中”=”也可以用于变量之间赋值(注意观察每个例子调用的函数),如下:
class Widget{ public: Widget(){} Widget(const Widget&){} Widget& operator=(const Widget&){} }; Widget w1; // 调用Widget的无参构造函数Widget() Widget w2 = w1; // 利用"="来初始化w2,这里调用了Widget的拷贝构造函数Widget(const Widget&) // 以下是变量间赋值的例子,因为w1在执行"="之前已经构造出来了,因此编译器视"="为赋值操作 w1 = w2; // w2通过"="赋值给w1,这里调用了Widget的赋值操作函数Widget& operator=(const Widget&)
相较于()和”=”,{}初始化方式有以下优势:
- 它的应用场景比()和”=”更加广泛:
// 可以使用{}为vector初始化几个元素,而用()和”=”是无法办到的 std::vector<int> v{ 1, 3, 5 };
- 对于内置类型,长字节类型转换成短字节类型(narrowing conversion),比如double转化成int,{}是不允许的:
double x, y, z; int sum1{ x + y + z }; // 编译错误 int sum2(x + y + z); // 编译通过,但是x + y + z的精度会丢失,比如0.5转化成了0 int sum3 = x + y + z; // 编译通过,但是x + y + z的精度会丢失,比如0.5转化成了0
- 用{}来初始化变量,可以避免Most Vexing Parse问题。C++有一条规则:当一条语句可以被编译器解析成函数申明(function declaration)时,编译器会优先将该语句视为函数申明,即使该语句的意图不是函数申明。编译器无意地将该语句解析成函数申明所带来的问题为Most Vexing Parse问题。下面通过例子来说明:
// 以下语句的意图是希望通过Widget的无参构造函数初始化一个类型为Widget的变量w。 // 但实际上,编译器会将以下语句理解成函数申明:函数名为w,没有入参,返回值为Widget。 Widget w(); // 编译器能通过,但并没有构造一个类型为Widget的变量w Widget w{}; // 编译器能通过,调用了Widget的无参构造函数初始化一个类型为Widget的变量w
用{}来初始化变量也会有以下问题:
- auto推导类型的时候会推导成std::initializer_list
auto x{1}; // auto -> int auto y = {1}; // auto -> std::initializer_list<int>
- 当存在重载的std::initializer_list构造函数,编译器会优先选择该构造函数
class A { public: A(int count, int value){} A(std::initializer_list<int> il){} }; A a{5, 0}; // 优先使用A(std::initializer_list<int> il)来初始化a
- 即便是有一个构造函数能够完美的匹配初始化参数,一旦有重载的std::initializer_list构造函数,那么编译器也会通过一系列隐式转换,来调用重载的std::initializer_list构造函数
class A { public: A(int count, int value){} A(std::initializer_list<double> il){} }; // 虽然以下初始化参数5,0能完美匹配第一个构造函数, // 但实际上,编译器会将5和0分别转换成double类型, // 进而调用A(std::initializer_list<int> il)来初始化a A a{5, 0};#c++##计算机求职##我的实习求职记录##我的求职思考#