Item7:区别使用()和{}创建对象

程序员小x大约 3 分钟C++effective modern c++读书笔记

Item7:区别使用()和{}创建对象

初始化的方式

c++常见的初始化方式有如下几种:

int x(0);               //使用圆括号初始化

int y = 0;              //使用"="初始化

int z{ 0 };             //使用花括号初始化

int z = { 0 };          //使用"="和花括号, 和只有花括号没有区别

对于像int这样的内置类型,研究两者区别就像在做学术,但是对于用户定义的类型而言,区别赋值运算符和初始化就非常重要了,因为它们涉及不同的函数调用:

Widget w1;              //调用默认构造函数

Widget w2 = w1;         //不是赋值运算,调用拷贝构造函数

w1 = w2;                //是赋值运算,调用拷贝赋值运算符(copy operator=)

甚至对于一些初始化语法,在一些情况下C++98没有办法表达预期的初始化行为。举个例子,要想直接创建并初始化一个存放一些特殊值的STL容器是不可能的(比如1,3,5)。

C++11使用统一初始化(uniform initialization)来整合这些混乱且不适于所有情景的初始化语法,所谓统一初始化是指在任何涉及初始化的地方都使用单一的初始化语法。 它基于花括号,出于这个原因我更喜欢称之为括号初始化。(译注:注意,这里的括号初始化指的是花括号初始化,在没有歧义的情况下下文的括号初始化指的都是用花括号进行初始化;当与圆括号初始化同时存在并可能产生歧义时我会直接指出。)统一初始化是一个概念上的东西,而括号初始化是一个具体语法结构。

括号初始化让你可以表达以前表达不出的东西。使用花括号,创建并指定一个容器的初始元素变得很容易:

std::vector<int> v{ 1, 3, 5 };  //v初始内容为1,3,5

括号初始化也能被用于为非静态数据成员指定默认初始值。C++11允许"="初始化不加花括号也拥有这种能力:

class Widget{private:
    int x{ 0 };                 //没问题,x初始值为0
    int y = 0;                  //也可以
    int z(0);                   //错误!
}

另一方面,不可拷贝的对象(例如std::atomic——见Item40)可以使用花括号初始化或者圆括号初始化,但是不能使用"="初始化:

std::atomic<int> ai1{ 0 };      //没问题
std::atomic<int> ai2(0);        //没问题
std::atomic<int> ai3 = 0;       //错误!

因此我们很容易理解为什么括号初始化又叫统一初始化,在C++中这三种方式都被看做是初始化表达式,但是只有花括号任何地方都能被使用。

总结

  • 花括号初始化是最广泛使用的初始化语法,它防止变窄转换,并且对于C++最令人头疼的解析有天生的免疫性
  • 在构造函数重载决议中,编译器会尽最大努力将括号初始化与std::initializer_list参数匹配,即便其他构造函数看起来是更好的选择
  • 对于数值类型的std::vector来说使用花括号初始化和圆括号初始化会造成巨大的不同
  • 在模板类选择使用圆括号初始化或使用花括号初始化创建对象是一个挑战。
Loading...