本文已参与「新人创造礼」活动,一起开启掘金创造之路
C++11新特性

怎么查看咱们现在运用的是什么 C++ 规范?

1、auto
- 能够从初始化表达式中推断出变量的类型,大大简化编程作业
- 归于编译器特性,不影响最终的机器码质量,不影响运转效率

2、decltype
- 能够获取变量的类型

3、nullptr(null pointer:空指针)
- 能够解决NULL的二义性问题

在 C++ 11 之前,NULL 既能代表指针、也能够代表整数 0 这样写代码会报错、
所以在 C++ 11 之后,咱们运用 nullptr 来特指指针。 NULL 为整数 0.
4、快速遍历:
之前的数组遍历:
int array[] = {0,1,2,3,4,5};
int i = 0;
for(i=0;i<sizeof(array); i++){
cout<< array[i] << endl;
}
C++ 11 之后的遍历:
- 咱们不需求理睬数组的大小,编译器会挨个将元素赋值给 item 元素。
int array[] = {0,1,2,3,4,5};
for(int item = array){
cout<< item << endl;
}
5、lambda表达式
lambda 表达式的结构很复杂,有很多种方式,它的本质便是一个函数。
创造它的意图:为了将函数的效果域,放到函数体里边。
(1)最简略的方式:[capture list] {function body} ,了解:
- 这个是一个特别的表达式,里边包括 捕获列表 + 函数实体
- 它的本质便是一个函数。

接下来给 lambda 表达式进行赋值
- 函数实体既没有返回值,也没有函数实体。
- 运用函数指针指向它。

更为简洁的赋值:

调用 lambda 函数实体

(2)添加函数的参数列表:[capture list] (params list) {function body}
- 中心添加了参数列表,这个函数的参数列表。

(3)添加函数的返回值:[capture list] (params list) -> return type {function body}
- -> return type ,return type 为这个函数的返回值类型。
- 可是 -> return type 能够进行省掉。

6、lambda 捕获变量
描绘:

解决:

也能够进行隐式捕获:(并且默认是 值捕获)

留意点:
(1)默认都是 值捕获:

(2)地址捕获 :将 a 的地址传递进来。

7、Lambda表达式 – mutable
问题描绘:假如咱们想在 lambda 表达式傍边,修正传进行来的变量值。
(1)运用 地址捕获:

(2)运用 mutable

留意:

C++14
1、泛型 lambda

2、对捕获的变量进行初始化

C++17
1、能够进行初始化的if、 switch语句 、
- 变量 a 只为 if else 服务
- 变量 b 只为 switch 服务

其他的新的语法特性还没有流行起来。
反常
在编程傍边咱们经常遇到的过错类型:
- 语法过错:编译器就会提示咱们
- 逻辑过错:是程序员自己留下的过错。
- 反常:在程序运转傍边或许呈现,也或许不呈现。
比如说在程序在运转进程傍边,内存空间不足。

这个状况比较夸张,可是在实践开发的时分,必定有或许呈现。
- 每个程序分别写自己的代码,请求的次数多了,必定会形成内存不足。
- 反常没有被处理,会导致程序停止 。

1、捕捉
运用 try 和 catch 指令:
1、将或许发生反常的代码,放到 try 里边去。
2、一旦发生反常,就会主动到对应的 catch 傍边履行代码。
- 从每次发生反常,都会履行 catch 傍边的代码

改善:咱们呈现反常之后,break 跳出。

分析流程:
- 假如没有 break ,在履行完循环之后,也会跳出循环。
- 有 break 的话,发生反常之后立马会调出循环。

2、主动抛出
-
上面的反常是系统本身抛出的反常。
-
有的时分,或许需求咱们自己去主动抛出反常。
举例:0 做被除数,咱们要主动抛出反常


catch 和 throw 的捕捉类型必须匹配。


总结:
1、将或许发生反常的代码,放到 try 里边去。
2、一旦发生反常,就会主动到对应的 catch 傍边履行代码。
反常抛出的声明:

3、自界说反常类型
- 经过类来界说反常

- 经过面向目标的思想,这有什么优势呢?
- 自己能够添加很多的与反常相关的成员函数。

4、规范反常



智能指针
传统指针存在的问题
- 需求手动办理内存
- 容易发生内存泄露(忘掉开释、呈现反常等)
- 开释之后发生野指针
内存走漏:该开释内存的时分,没有开释,形成内存越来越少。

指针的开释必须放到最终,并且还得赋值为 nullptr ,避免野指针。

智能指针就不需求这么做:
- auto_ptr 是一个模板类,需求传入指向的目标类型
- p 是一个目标,相当于将 newPerson()的返回值传给 p 的构造函数。
- p 在栈空间,当 test( ) 履行完毕之后,p就会被开释,然后堆空间的内容也会跟着开释。

留意:
-
智能指针创造的意图:在栈空间的指针毁掉时,将堆空间的内存进行开释。
-
智能指针必须指向堆空间的目标,由于栈空间的目标不需求咱们智能指针进行开释,系统会主动开释。
-
假如指向栈空间的目标,那么就会形成二次开释。
智能指针便是为了解决传统指针存在的问题
- auto_ptr:归于C++98规范,在C++11中现已不引荐运用(有缺点,比如不能用于数组)
- shared_ptr:归于C++11规范
- unique_ptr:归于C++11规范
1、自己完成智能指针
意图:在自己毁掉的时分,开释堆空间。
初步完成:

缺点:
- p1 并不是指针,而是一个目标,真实的指针是 m_pointer 成员变量。
- 所以很不方便。

改善:将运算符进行重载

存在的问题:不能存放数组。
- 不能开释全部元素

改善:

2、shared_ptr
hared_ptr 的规划理念:
1、多个shared_ptr能够指向同一个目标,当最终一个shared_ptr在效果域范围内完毕时,目标才会被主动开释。
能够经过一个已存在的智能指针初始化一个新的智能指针
- p1 、 p2、p3、p4 都毁掉的时分, new 的Person 内存才能够进行开释。


2、针对数组的用法

3、一个shared_ptr会对一个目标发生强引证(strong reference)
- 每个目标都有个与之对应的强引证计数,记录着当时目标被多少个shared_ptr强引证着
- 能够经过shared_ptr的 use_count 函数获得强引证计数
- 当有一个新的shared_ptr指向目标时,目标的强引证计数就会+1
- 当有一个shared_ptr毁掉时(比如效果域完毕),目标的强引证计数就会-1
- 当一个目标的强引证计数为0时(没有任何shared_ptr指向目标时),目标就会主动毁掉(析构)
多一个指向,强引证个数就会 + 1.少一个,就会减一

两次开释的状况:

4、shared_ptr的循环引证
会导致内存走漏:
-
Person 类傍边有一个Car 类型的智能指针, m_car (成员变量)
-
Car 类傍边有一个 Person 类型的智能指针, m_person (成员变量)
-
在主函数,栈空间傍边创建两个目标,Person 目标 ,Car 目标。
没有循环调用的状况:

有循环调用的状况:(你中有我,我中有你)
- 缺点:内存走漏,堆空间无法开释

内存分布图:
-
Person 目标傍边的 m_car 指向 Car
-
Car 目标傍边的 m_person 指向 Person
-
Car 和 Person 的强引证计数都是 2

- 当栈空间开释之后,
- Car 和 Person 的强引证计数都是 1 ,由于还有强引证计数的存在,所以堆空间不会毁掉
- shared_ptr 会发生强引证,只要有一个引证没有开释,那么就不会毁掉堆空间。

解决办法:运用弱引证 weak_ptr
3、weak_ptr
- weak_ptr会对一个目标发生弱引证
- weak_ptr能够指向目标,解决shared_ptr的循环引证问题

内存空间:

4、unique_ptr
- unique_ptr也会对一个目标发生强引证,它能够确保同一时间只要1个指针指向目标
- 当unique_ptr 毁掉时(效果域完毕时),其指向的目标也就主动毁掉了
- 能够运用std::move函数转移unique_ptr的所有权
了解即可