本文已参与「新人创造礼」活动,一起开启掘金创造之路
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的所有权
了解即可