C++11以后,对象的初始化可以有多种选择,总的来说,初始值可以通过三种方式给出:(), = 和 {}

int x(0);   //initializer is in parentheses
int y = 0;  //initializer follows "="
int z{ 0 }; //initializer is in braces

使用{}的初始化方式称为uniform initialization,或者又称为braced initialization. Braced initialization适用于任何对象初始化的场景,

// 指定container的初始内容
std::vector<int> v{ 1, 3, 5 }; // v's initial content is 1, 3, 5// 指定非static成员对象的默认值
class Widget
{...
private:int x{ 0 };  // OKint y = 0;   // OKint z(0);    // error!
};// uncopyable对象的初始化
std::atomic<int> ai1{ 0 };  // OK
std::atomic<int> ai2(0);    // OK
std::atomic<int> ai3 = 0;   // error!

Braced initialization的一个新特性是它可以禁止内置类型之间隐式的narrowing conversion。使用 () 和 = 不会检查narrowing conversion。

double x, y, z;
int sum1{ x + y + z }; // error!
int sum2( x + y + z ); // OK
int sum3 = x + y + z;  // OK

Braced initialization的另一个特性是它可以避免C++中将对象的默认构造转换成函数声明的情况,

Widget w2(); // 声明一个函数w2, 返回值是Widget
Widget w3{}; // 声明一个Widget对象并调用Widget默认构造函数

Braced initialization的缺点:

1.当使用braced initialization初始化auto变量时,类型推断是std::initializer_list而不是你期望的类型,

auto x1 = 27;     // type is int, value is 27
auto x2(27);      // type is int, value is 27
auto x3 = { 27 }; // type is std::initializer_list<int>, value is {27}

2.如果类的其中一个构造函数的形参是std::initializer_list,那么如果用braced initialization初始化对象,编译器会尽最大可能地调用形参是std::initializer_list的构造函数。

class Widget
{
public:Widget(int i, bool b);Widget(int i, double d);Widget(std::initializer_list<long double> il);...
};Widget w1(10, true); //调用第一个构造函数
Widget w2{10, true}; //调用std::initializer_list构造函数,10和true转换成long doubleWidget w3(10, 5.0);  //调用第二个构造函数
Widget w4{10, 5.0};  //调用std::initializer_list构造函数,10和5.0转换成long double

甚至原本是copy构造函数和move构造函数的地方,都会被转换成使用std::initializer_list的构造函数,

class Widget
{
public:Widget(int i, bool b);Widget(int i, double d);Widget(std::initializer_list<long double> il);operator float() const;...
};Widget w5(w4);   //调用copy构造函数
Widget w6{w4};   //调用std::initializer_list构造函数,w4被转换成float,float被转换成long doubleWidget w7(std::move(w4));  //使用move构造函数
Widget w8{std::move(w4)};  //使用std::initializer_list函数,w4被转换成float,float被转换成long double

更有甚者,编译器使用std::initializer_list构造函数的决心是如此之大以至于明明不可以转换成std::initializer_list构造函数,它也会去调用,最终导致编译无法通过,

class Widget
{
public:Widget(int i, bool b);Widget(int i, double d);Widget(std::initializer_list<bool> il);...
};Widget w{10, 5.0}; // error! 导致narrowing conversion

编译器会忽略前两个构造函数,并调用std::initializer_list构造函数。但这需要将int(10)和double(5.0)转换成bool,这会导致narrowing conversion。然而narrowing conversion在braced initialization中是禁止的,所以调用非法,编译无法通过。

有没有可能既声明了std::initializer_list构造函数,但是编译器还是调用其他普通的构造函数呢?答案是除非braced initialization中参数的类型无法转换成std::initializer_list中的类型。

class Widget
{
public:Widget(int i, bool b);Widget(int i, double d);Widget(std::initializer_list<std::string> il);...
};//没办法将 int 和 bool 转换成 std::string
Widget w1{10, true}; //调用第一个构造函数
Widget w2{10, 5.0};   //调用第二个构造函数

如果你使用一个空的{}来构造一个既支持默认构造函数又支持std::initializer_list构造函数的类对象,那么最终调用的是哪一个呢?答案是调用默认构造函数,因为空的{}意味着没有参数,并不是一个空的std::initializer_list,

class Widget
{
public:Widget();Widget(std::initializer_list<int> il);...
};Widget w1;   //调用默认构造函数
Widget w2{}; //调用默认构造函数
Widget w3(); //声明一个函数!

如果想用空的{}调用std::initializer_list构造函数,可以将空的{}放进()或{}里,

Widget w4({});  // 调用std::initializer_list构造函数,赋值empty list
Widget w5{{}};  // 同上

翻译整理自:《Effective Modern C++》Chapter3 Moving to Modern C++

[C++] - 创建对象时 () 和 {} 的区别相关推荐

  1. Delphi创建对象时,Application、Self、nil三者的区别

    Delphi创建对象时,Application.Self.nil三者的区别 ***.Create(AOwner:TComponent); //AOwner:创建者Create(nil);//这种方式创 ...

  2. Java检查异常、非检查异常、运行时异常、非运行时异常的区别

    Java检查异常.非检查异常.运行时异常.非运行时异常的区别 参考文章: (1)Java检查异常.非检查异常.运行时异常.非运行时异常的区别 (2)https://www.cnblogs.com/ou ...

  3. 创建对象时引用的关键字,assign,copy,retain

    创建对象时引用的关键字: assign: 简单赋值,不更改索引计数(强引用) copy: 建立一个索引计数为1的对象,然后释放旧对象 retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对 ...

  4. python构造函数在创建对象时,没有自动执行,object has no attribute

    新手踩坑,python构造函数在创建对象时,没有自动执行,object has no attribute 刚开始学python,照着书敲,就离谱,一直在报错object has no attribut ...

  5. 关于java中创建对象时属性的初始化过程

    java是一种面向对象的编程语言,那么了解创建对象时程序会怎么执行就变得尤为重要,下面我们就一起看看在我们使用new关键字创建对象时是怎么对属性初始化的: 下面是一个Person类,其中有成员变量ag ...

  6. SAP中非评估收货或不收货与收货时的应用区别和记账差异

    采购订单的收货和发票校验操作是会关联财务的记账.基于采购订单的业务情形不同,就有不同的设定.比如有的采购订单是不需要收货的:有的采购订单需要收货:有的采购订单又是非评估收货:那么这些采购订单中的控制点 ...

  7. 【MATLAB】基本绘图 ( 句柄值 | 对象句柄值获取 | 创建对象时获取句柄值 | 函数获取句柄值 | 获取 / 设置 对象属性 | 获取对象属性 )

    文章目录 一.对象句柄值获取 1.句柄值 2.创建对象时获取句柄值 3.函数获取句柄值 4.获取 / 设置 对象属性 二.获取对象属性 1.获取 线 对象属性 2.获取 坐标轴 对象属性 一.对象句柄 ...

  8. python 创建对象时自动调用的函数_Python自动测试(6)——类和对象,python,自动化,六类...

    类和函数对象概念 类 :同一类的事物,是个抽象的概念(属性.方法) 对象 :符合类描述的具体存在的 例如把电脑当做是一个类,然后你现在所使用的具体存在的电脑就是对象. 为什么要封装类?举个例子,ATM ...

  9. 无参构造函数和有参构造函数在创建对象时初始化的使用

    类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行. 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void.构造函数可用于为某些成员变量设置初始值. 默认 ...

最新文章

  1. Windows下安装Z3的Python3版
  2. ACM提交,C++,G++,C,GCC的区别
  3. 白话科普,10s 了解 API
  4. Python中各个模块的介绍和使用
  5. macOS安装Telnet
  6. 十个谈话技巧让你在IT职场出人头地
  7. (15)HTML面试题集锦
  8. 公司聚餐完毕,明日启程回家过年
  9. sphinx 全文检索 笔记一
  10. php简单多态,PHP 对象 多态性 简单图形计算器
  11. erlang中遍历取出某个位置的最大值
  12. 基于stc15f2k60s2芯片单片机编程(按键控制)
  13. 一、Java快速入门
  14. 自制 arduino 音符频率对照表(音符在arduino里对应的值)
  15. apk加壳加密工具(apk protect) v1.0下载
  16. Ps UI设计如何简单快捷切图
  17. iOS 屏幕旋转 强制旋转
  18. 2021-10-07 浊音,清音,爆破音频谱分析
  19. 遗传算法(四)——交叉、变异与替换
  20. windows下安装openssl工具及生成pfx文件

热门文章

  1. Mac neo4j忘记密码,不删除数据处理方法
  2. 解决python报错写入文件 io.UnsupportedOperation: not writable
  3. Simpy练习案例(一):基本线性流程仿真
  4. cubic差值matlab,matlab自带的插值函数interp1的四种插值方法
  5. 康力电梯开门不关门 服务器显示开门键动作,康力电梯现场调试后常见问题及排除...
  6. Kubernetes-保障集群内节点和网络安全
  7. Java基础-----选择结构(一)
  8. 一群喵星人把他家包围了。。
  9. wget命令3(转载)
  10. SQl---基础整理6--数据库的创建