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++##计算机求职##我的实习求职记录##我的求职思考#
全部评论

相关推荐

点赞 评论 收藏
分享
4 3 评论
分享
牛客网
牛客企业服务