[C++] - 创建对象时 () 和 {} 的区别
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++] - 创建对象时 () 和 {} 的区别相关推荐
- Delphi创建对象时,Application、Self、nil三者的区别
Delphi创建对象时,Application.Self.nil三者的区别 ***.Create(AOwner:TComponent); //AOwner:创建者Create(nil);//这种方式创 ...
- Java检查异常、非检查异常、运行时异常、非运行时异常的区别
Java检查异常.非检查异常.运行时异常.非运行时异常的区别 参考文章: (1)Java检查异常.非检查异常.运行时异常.非运行时异常的区别 (2)https://www.cnblogs.com/ou ...
- 创建对象时引用的关键字,assign,copy,retain
创建对象时引用的关键字: assign: 简单赋值,不更改索引计数(强引用) copy: 建立一个索引计数为1的对象,然后释放旧对象 retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对 ...
- python构造函数在创建对象时,没有自动执行,object has no attribute
新手踩坑,python构造函数在创建对象时,没有自动执行,object has no attribute 刚开始学python,照着书敲,就离谱,一直在报错object has no attribut ...
- 关于java中创建对象时属性的初始化过程
java是一种面向对象的编程语言,那么了解创建对象时程序会怎么执行就变得尤为重要,下面我们就一起看看在我们使用new关键字创建对象时是怎么对属性初始化的: 下面是一个Person类,其中有成员变量ag ...
- SAP中非评估收货或不收货与收货时的应用区别和记账差异
采购订单的收货和发票校验操作是会关联财务的记账.基于采购订单的业务情形不同,就有不同的设定.比如有的采购订单是不需要收货的:有的采购订单需要收货:有的采购订单又是非评估收货:那么这些采购订单中的控制点 ...
- 【MATLAB】基本绘图 ( 句柄值 | 对象句柄值获取 | 创建对象时获取句柄值 | 函数获取句柄值 | 获取 / 设置 对象属性 | 获取对象属性 )
文章目录 一.对象句柄值获取 1.句柄值 2.创建对象时获取句柄值 3.函数获取句柄值 4.获取 / 设置 对象属性 二.获取对象属性 1.获取 线 对象属性 2.获取 坐标轴 对象属性 一.对象句柄 ...
- python 创建对象时自动调用的函数_Python自动测试(6)——类和对象,python,自动化,六类...
类和函数对象概念 类 :同一类的事物,是个抽象的概念(属性.方法) 对象 :符合类描述的具体存在的 例如把电脑当做是一个类,然后你现在所使用的具体存在的电脑就是对象. 为什么要封装类?举个例子,ATM ...
- 无参构造函数和有参构造函数在创建对象时初始化的使用
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行. 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void.构造函数可用于为某些成员变量设置初始值. 默认 ...
最新文章
- Windows下安装Z3的Python3版
- ACM提交,C++,G++,C,GCC的区别
- 白话科普,10s 了解 API
- Python中各个模块的介绍和使用
- macOS安装Telnet
- 十个谈话技巧让你在IT职场出人头地
- (15)HTML面试题集锦
- 公司聚餐完毕,明日启程回家过年
- sphinx 全文检索 笔记一
- php简单多态,PHP 对象 多态性 简单图形计算器
- erlang中遍历取出某个位置的最大值
- 基于stc15f2k60s2芯片单片机编程(按键控制)
- 一、Java快速入门
- 自制 arduino 音符频率对照表(音符在arduino里对应的值)
- apk加壳加密工具(apk protect) v1.0下载
- Ps UI设计如何简单快捷切图
- iOS 屏幕旋转 强制旋转
- 2021-10-07 浊音,清音,爆破音频谱分析
- 遗传算法(四)——交叉、变异与替换
- windows下安装openssl工具及生成pfx文件
热门文章
- Mac neo4j忘记密码,不删除数据处理方法
- 解决python报错写入文件 io.UnsupportedOperation: not writable
- Simpy练习案例(一):基本线性流程仿真
- cubic差值matlab,matlab自带的插值函数interp1的四种插值方法
- 康力电梯开门不关门 服务器显示开门键动作,康力电梯现场调试后常见问题及排除...
- Kubernetes-保障集群内节点和网络安全
- Java基础-----选择结构(一)
- 一群喵星人把他家包围了。。
- wget命令3(转载)
- SQl---基础整理6--数据库的创建