参考资料
碎碎念:实在是太复杂了,看到 45 分钟就直接放弃了,也立刻理解了为什么大家说“C++ 是刀耕火种的语言”。C++ 为了兼容性舍弃了太多易理解性。
概述
C++ 的初始化规则由继承自 C 语言的初始化规则、C++ 03 前的规则、现代 C++ 的初始化规则等三部分组成。
几种初始化
直接初始化:T t1(1, 2, 3);
拷贝初始化:T t2 = t1;
默认初始化:T t3;
vacuous initialization:int x; // 对于标量,在 C 里,这样仅是赋予了未定义值,但是在 C++ 里,会执行默认初始化。
值初始化:(此定义在后文中)
> 为什么 T t(); 会被编译器理解成函数?这是为了与 C 语言的兼容。为此,编译器会优先将此理解为函数,而不是变量的初始化。
>>>>>但是在值初始化列表里,是可以这么初始化的。
易混淆点摘要
1. 不用混淆初始化和赋值
一个类对象在初始化时,一定是调用其构造函数,而不是赋值函数。
赋值这一情形当且仅当在覆写值时发生。
应用程序员通常不用区分直接初始化和拷贝初始化,但编译器需要。
应用程序员需要在什么时候考虑这一点,以及为什么考虑这一点:
- 会影响到类型转换。尤其是没有被声明为
explicit构造函数,这类函数可以被称为转换构造函数。
2. 值初始化的规则
C++03 里可提升的地方,也是在随后的标准里改善的地方
现代 C++ 的值初始化
直接列表初始化允许隐式转换,而拷贝列表初始化不允许
尝试复现后,我发现我的复制列表初始化复现并没有如 PPT 所示一般通过,而是被拒绝了收缩转换。
现代 C++:初始化器列表
使用初始化器列表时,往往是通过值传递(pass by value)的。拷贝初始化器列表会产生浅拷贝。
如果一个类既有默认构造函数,也有基于初始化器列表的构造函数,那么 {} 将会优先采用默认构造函数;如果一个类只有基于初始化器列表的构造函数,那么 {} 将会采用基于初始化器列表的构造函数。
通常,基于初始化器列表的构造函数会出现在想要使用模板的类中。
在类中加入基于初始化器列表的构造函数有时会混淆已有的初始化行为。如下图中,c1 初始化时采用的函数在加入基于初始化器列表的构造函数前后发生了变化: