C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>

一、一致性初始化(uniform initialization)

  之前初始化时存在多个版本,让使用者使用时比较混乱,现在提供一种万用的初始化方法,就是使用大括号。

  原理解析:当编译器看到大括号包起来的东西时,会生成一个initializer_list(initializer_list它其实是关联一个array),然后再调用构造函数时,一个个从array分解取出来然后调用构造函数,但是如果这个函数自身提供了initializer_list参数类型的构造函数时,则不会分解而是直接传过去。

 C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第1张图片

// 初值列:强迫初始化为 0 (或nullptr).
int i; // i 初始化为未定义值.
int j{}; // j 初始化为 0 (大括号可以用来设初值)
int * p; // p 初始化为未定义值.
int * q{}; // q 初始化为 0 (大括号可以用来设初值)

// 窄化(精度降低或造成数值变动)对大括号而言是不成立的.
int x0(3.4); // ok.
int x1 = 3.4; // ok.
int x2 { 3.4 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)
int x3 = { 3.4 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)
std::vector v1 { 1, 2, 3 }; // ok.
std::vector v2 { 1.1, 2.2, 3.3 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)

二、Initializer_list

C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第2张图片

C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第3张图片

注意:只要编译器遇到大括号里面有一些数,再传值的时候都会去生成一个initializer_list去处理,这个和前面一章节提到的不定参数模板相比,这个必须类型要一致,而后者则可以类型随意组合

三、for循环

1、2.0新增新语法本质是将后边的集合取出来依次赋值给前面申明的变量

C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第4张图片

C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第5张图片

 注意:

  1. 上图中申明引用类型的速度快很多,因为引用相当于指针的操作只操作4个字节,而非引用的速度开销随着数据类型所占空间增长而增大,且声明引用后的修改将直接影响到集合中数据的值;
  2. 用for操作容器时,标准库规定,关联容器都不允许通过迭代器修改修改容器的值,也就是说上面申明为引用的for循环无法修改关联容器里的数值(因为关联容器的迭代器是const);
  3. 当解引用一个关联容器迭代器时,会获得一个类型为value_type的值的引用。对于map而言,value_type是一个pair,pair由first和second组成,first成员保存const关键字,second保存值,而对于set而言,set的迭代器也是const,虽然set定义了iterator和const_iterator类型,但是两种类型都只允许只读set的元素;
  4. 因为set使用红黑树做底部结构,但是set只有一个元素(key和data是一样得,也就是说key就是value),所以set是不允许改变元素值得,所以在实现上,set的迭代器拿到的是const的iterator,是不能修改的;
  5. 我们通常不去对关联容器使用泛型算法,因为set的关键字是const的,而map的元素pair的第一个成员也是const的,因此不能将关联容器传递给修改或重排元素的算法(实际中我们使用泛型算法一般只把关联容器当成我们要操作数据的源头位置或者目的位置,例如copy算法将元素从一个关联容器拷贝到另一个序列)。

2、但对于explicit类型申明的转换是不可以的

C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第6张图片

四、explicit

  explicit关键字一直存在,只能作用在构造函数中, 目的是阻止编译器进行不应该允许的构造函数进行隐式转换(也就是说不让编译器自作聪明),声明为explicit的构造函数不能进行隐式转换,只能允许使用者明确调用构造函数;

  在C++2.0中,explicit可以支持不止一个参数的构造函数使用(之前只能支持传入一个实参)

1、2.0以前,explicit只能作用在一个实参的构造函数上

 1 #include 
 2 class Single{
 3 
 4 public:
 5     //普通构造函数(单一实参)
 6     Single(int a,int b = 0):num(a)
 7     {}
 8 private:
 9     int num;
10 };
11 
12 class SingleMore {
13 public:
14     //explicit 显示申明构造函数(单一实参)
15     explicit SingleMore(int a) :num(a)
16     {}
17 private:
18     int num;
19 };
20 
21 
22 #if 1
23 int main(int argc, char* argv[])
24 {
25     Single single(3);
26     Single single2 = 4;
27 
28     SingleMore singleMore(3);
29     SingleMore singleMore2 = 4;//编译报错,E0415 不存在从 "int" 转换到 "SingleMore" 的适当构造函数
30 
31     return 0;
32 }
33 #endif

2、2.0以后explicit可以适用多个实参的构造函数

 1 struct A
 2 {
 3     A(int) { }      // 转换构造函数
 4     A(int, int) { } // 转换构造函数 (C++11)
 5     operator bool() const { return true; }
 6 };
 7  
 8 struct B
 9 {
10     explicit B(int) { }
11     explicit B(int, int) { }
12     explicit operator bool() const { return true; }
13 };
14  
15 int main()
16 {
17     A a1 = 1;      // OK :复制初始化选择 A::A(int)
18     A a2(2);       // OK :直接初始化选择 A::A(int)
19     A a3 {4, 5};   // OK :直接列表初始化选择 A::A(int, int)
20     A a4 = {4, 5}; // OK :复制列表初始化选择 A::A(int, int)
21     A a5 = (A)1;   // OK :显式转型进行 static_cast
22     if (a1) ;      // OK :A::operator bool()
23     bool na1 = a1; // OK :复制初始化选择 A::operator bool()
24     bool na2 = static_cast<bool>(a1); // OK :static_cast 进行直接初始化
25  
26 //  B b1 = 1;      // 错误:复制初始化不考虑 B::B(int)
27     B b2(2);       // OK :直接初始化选择 B::B(int)
28     B b3 {4, 5};   // OK :直接列表初始化选择 B::B(int, int)
29 //  B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)
30     B b5 = (B)1;   // OK :显式转型进行 static_cast
31     if (b2) ;      // OK :B::operator bool()
32 //  bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool()
33     bool nb2 = static_cast<bool>(b2); // OK :static_cast 进行直接初始化
34 }

 C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>_第7张图片

 

你可能感兴趣的:(C++2.0新特性(二)——<一致性初始化、Initializer_list 、for循环、explicit>)