四、语言特性之<一致性初始化、Initializer_list 、基于范围的for循环、explicit>

目录

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

二、Initializer_list

1、initializer_list使用举例:

2、initializer_list源码剖析:

3、initializer_list在STL中的使用:

4、容器array:

三、基于范围的for循环

1、C++2.0新增新语法本质是将后边的集合取出来依次赋值给前面申明的变量(基于范围的for循环背后的原理)

2、基于范围的for循环对于explicit类型申明的转换是不可以的

四、explicit

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

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







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

C++11之前初始化时存在多个版本{},(),=。,让使用者使用时比较混乱C++11提供一种万用的初始化方法,就是使用大括号{}。

原理解析

当编译器看到大括号包起来的东西{t1,t2...tn}时,会生成一个initializer_list<T>initializer_list其实是关联一个array<T,n>)。

  • 调用函数(例如构造函数ctor)时该array内的元素可被编译器分解逐一传给函数;元素逐一分解传递给函数进行初始化

  • 但是如果调用函数自身提供了initializer_list<T>参数类型的构造函数时,则不会分解而是直接传过去直接整包传入进行初始化。所有的容器都可以接受这样的参数

 

举例:

// 初值列:强迫初始化为 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<int> v1 { 1, 2, 3 }; // ok.
std::vector<int> v2 { 1.1, 2.2, 3.3 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)

注:{}不允许窄化转化(类似不允许隐式类型转化)。

二、Initializer_list

1、initializer_list<T>使用举例

  • initializer_list<T>是一个class(类模板),这个必须类型要一致,跟模板不定的参数类型相比,模板不定的参数类型可以都不一样。

  • initializer_list<T>类似于容器的使用方法

(1)例1

(2)例2

注意:

  • 如果没有版本2,只有版本1。q和s不变,但是会分为两个参数传入。r将会不成立。

  • 只要编译器遇到大括号里面有一些数,再传值的时候都会去生成一个initializer_list<T>去处理;

  • initializer_list<T>这个和前面一章节提到的不定参数模板相比,这个必须类型要一致,而后者则可以类型随意组合

2、initializer_list源码剖析:

  • 1.initializer_list<T>背后有array数组支撑,initializer_list它其实是关联一个array<T,n>

  • 2.array是个指针,只是一个浅拷贝动作,比较危险,两个指针指向同一个内存

3、initializer_list在STL中的使用:

  • 所有容器都接受指定任意数量的值用于构造或赋值或者insert()或assign()

  • 算法max()和min()也接受任意参数

使用举例:

4、容器array:

TR1版本:

GCC4.9版本:

三、基于范围的for循环

1、C++2.0新增新语法本质将后边的集合取出来依次赋值给前面申明的变量(基于范围的for循环背后的原理)

 注意:

  1. 上图中申明引用类型的速度快很多,因为引用相当于指针的操作只操作4个字节,而非引用的速度开销随着数据类型所占空间增长而增大,且声明引用后的修改将直接影响到集合中数据的值;但是带来的后果是可以改变容器的值,如果不希望被改变,则可以加const。

  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、基于范围的for循环对于explicit类型申明的转换是不可以的

四、explicit

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

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

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

 1 #include <iostream>
 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、C++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 }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值