26-C++11/14(侯捷)

本文详细介绍了C++11及后续版本引入的新特性,包括变量模板、一致初始化、显式关键字、范围基For循环、右值引用、完美转发、移动构造和赋值等。这些特性提升了代码的效率、可读性和安全性,为C++程序员提供了更强大的工具箱。

1、演化、环境与资源

在这里插入图片描述

std命名空间,包含即可;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考书籍:
在这里插入图片描述

2、variadic Templates(数量不定的模板参数)

--表示可以接受任意变化的东西;
c++2.0还可以表示一包东西;

在这里插入图片描述
const Type&…args 可以传入任意的模板参数
在这里插入图片描述

  • 当参数包变为0时,需要写一个函数专门针对参数为空时进行处理;这里是print();
  • 想知道参数包时多少个时:sizeof…(args)

上面数量不定的模板参数有里两种的表达方式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

tuple		--元组,里面可以放置任意参数类型,任意数量;

3、Spaces in Template Expressions(模板表达式中的空格)

在c++11之前,模板之间有模板,必须用空格将其隔开;
vector<list<int> >;
c++11出现后,就不需要加空格了
vector<list<int>>;

在这里插入图片描述

4、nullptr和std::nullptr_t

nullptr: c++11允许使用nullptr来代替0 or NULL

std::nullptr的类型也是nullptr

在这里插入图片描述

5、auto关键字

使用auto特别有用在type很长,很复杂;

在这里插入图片描述

auto后面,必须要写东西,让编译器能够推导;
auto i;(错误)

使用场合:

  • lambda函数:没有名称的函数,仿函数

​ lambda表达式,很难写出它的类型,用auto;

  • 迭代器使用auto关键字;

在这里插入图片描述
在这里插入图片描述

differnce_type就是两个迭代器之间的距离,通过萃取机进行萃取。

6、Uniform Initialization—一致性的初始化

c++11之前,程序员对变量、对象的初始化怎么写比较头疼;因为初始化可能发生在:(),{},=赋值符号;
在这里插入图片描述

因此,c++11导入了一致性初始化,就是任何初始化语言都可以用一种通用语法,就是大括号{},在变量的后面直接放初始值

7、explicit

在这里插入图片描述

编译器自己会进行一个隐式的转换,将5转换为5+0*i  
如果不想这样自动转换;在构造函数前面加explicit

在这里插入图片描述

8、range-based for

将coll容器中的元素一个个拿出来哦,放入decl变量中;

在这里插入图片描述
在这里插入图片描述

auto:在块作用域、命名作用域、循环初始化语句等等 中声明变量时,关键词auto用作类型指定符。
    
-- auto
autofor(auto x:range) 这样会拷贝一份range元素,而不会改变range中元素;
    
-- auto&
当需要修改range中元素,用for(auto& x:range)
    
-- const auto&
当只想读取range中元素时,使用const auto&,如:for(const auto&x:range),它不会进行拷贝,也不会修改range\
    
-- const auto
当需要拷贝元素,但不可修改拷贝出来的值时,使用 for(const auto x:range),避免拷贝开销

9、=default,=delete

构造函数,如果你没写,编译器会给你一个;
如果写了,编译器就不会给你默认的构造函数;
但是如果我还想要系统提供的构造函数,尽管系统提供的构造函数什么都没做;在后面加上=default
=delete: 表示删除这个函数;
=default: 表示还是使用编译器给我默认提供的;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构造函数可以重载;
拷贝构造函数只能有一个(不能进行重载);
拷贝赋值函数也不能重载;

在这里插入图片描述
在这里插入图片描述

10、Alias Template(template typdef)—模板的化名

Alias:化名,别名

使用using来取别名,define和typedef都不可以(没办法指定参数)。
在这里插入图片描述

11、template template parameter—模板模板参数

在这里插入图片描述

上面基本语法是对的,但是使用时还是出错。

因为vector在模板模板参数中使用时,编译器无法自己给vector第二个参数分配默认值,在外面单独使用时可以。
在这里插入图片描述

使用using解决,将vector的第二个参数进行封装。

using Vec = vector<T,allocate<T>>;

在这里插入图片描述

12、Type Alias(similar to typedef)—类型的化名

typedef void(*func)(int);	--c语言,是一个函数指针
using func = void(*)(int,int);	--func是一个函数指针,非常的清楚
函数的名称就是函数的地址,就是函数指针。

在这里插入图片描述

using出现的可能性:
在这里插入图片描述

13、noexcept

保证不会丢成异常,noexcept放在函数小括号的后面,大括号的前面

noexcept(noexcept(x.swap(y)))	--后面小括号保证是复合某个条件不会丢出异常
	符合x.swap(y)函数不丢异常的话,外面的函数就不丢异常

在这里插入图片描述

异常一定要被处理,异常如果一直没有处理的话,会一直向上处理,最后程序就会abort(),结束程序。

和move有关的右值引用:

在这里插入图片描述

必须通知vector它的移动构造和移动赋值都是noexcept的,这样才能让vector进行两倍的容量成长。

vector在进行扩容时间,调用移动构造(设置move系列函数时,移动构造,移动赋值,一定不能出现错误,且要告诉我们这个没有发生异常的)(因此需要告诉vector,放心的调用这个函数)

只有vector会大幅度的成长。

14、override—复写,改写

override:复写,改写,运用在虚函数上。

函数的声明一定要完全相同。

在虚函数后面表名override,如果写错了,编译器就会提醒你;
在这里插入图片描述

**注意:**这里并没有复写,是两个函数;
在这里插入图片描述

但是,如果你不小心写错了,编译器会认为它是两个函数,而不是子类重写父类的函数;

c++2.0时,我们可以加上override关键字,如果不是重写的话,编译器会进行报错;

在这里插入图片描述

15、final

针对类: 表示我是继承体系的最后一个,不要再有人继承我了;
针对虚函数: 添加virtual关键字之后,虚函数不可以被复写;

在这里插入图片描述

16、decltype

使用decltype可以找到一个变量、表达式的类型;

在这里插入图片描述
在这里插入图片描述

作用:
1. 声明返回值类型:
	lambda:就是临时写出的一段函数,没有名称;
	注意:传进来的必须是一个容器;(传进去的是非容器,就会没有迭代器;)

在这里插入图片描述
在这里插入图片描述

2、元编程

在这里插入图片描述

3、用在lamada表达式中,decltype(cmp):表示推导变量的类型

在这里插入图片描述

17、Lambdas

c++11使用Lambdas允许你定义inline 函数,可以被用来当做一个东西(参数或者object)

注意: Lambda表达式是一个inline Function

Lambda可以你临时想用来做一些东西,直接写Lambda表达式后,就可以直接拿来用。

比如排序时,我们会写一个小的东西(直接使用Lambda表达式),来说明排序的准则,是从大到小函数从小到大.
在这里插入图片描述
在这里插入图片描述

[]:看到它就知道是Lambda,用来取用外部的变量;

  • pass by value[变量名]
  • pass by reference[&变量名]
  • [=]表示可以使用所有的外部变量

():里面放的是参数;

**{}:**函数本体;

  1. **throwSpec:**函数是否可以抛出异常;
  2. **mutable:**影响着[]中的数据是否可以被改写;
  3. **->reType:**表示返回值类型

如果上面三个不写,()可写可不写;有一个写了,就要写()。
在这里插入图片描述

**左边程序:**就是直接操作的是外面的id,因为加了mutable,不是操作拷贝的id,直接对传入的全局变量进行操作。

**右边程序:**如果没有mutable,直接对()进行重载。在这里插入图片描述
没加mutlable,{}里面不可以进行++id,传入的参数是不可以改变的;

对于需要传入排序的准则,写成一个仿函数(类)是一个更好的选择;
在这里插入图片描述

Lambda表达式和仿函数的使用对比:
在这里插入图片描述

标准库,其他使用Lambda表达式的地方:
在这里插入图片描述

20、右值引用

会在move中使用,move语义对容器的性能有大幅度的改善;

新的引用类型;

会帮助解决不必要的copy;

右边是一个右值,左手边接收的可以去"偷"右边资源,而不需要重新分配内存。

左值:变量就是左值, 可以出现在=的左侧,也可以出现在右侧;

右值: 不能放在=的左边,只能出现在=的右边;(最常见的右值就是临时对象)
在这里插入图片描述

上面string和complex类推翻了右值,不管他。

a+b是一个右值;

函数返回的东西是一个右值,对右值取地址是不行的;
在这里插入图片描述

拷贝构造会分配另一块内存(多消耗内存):
在这里插入图片描述

Vtype(buf): 临时对象,是一个右值。

move就是一个指针的浅拷贝,move之后原来的就不可以使用了。

右值(传入的参数是右值,说明允许被偷),编译器一定会用偷的方式(move,移动构造)进行构造;
在这里插入图片描述

如果是左值,也想要要用偷的方式进行操作,使用move函数(将左值变成右值);
在这里插入图片描述

vector中insert的源代码:
在这里插入图片描述

21、Perfect Forwarding—完美转发

在这里插入图片描述

上面虚线的过程在转发的过程中可能会遗漏一些数据信息。在这里插入图片描述

类中有指针数据,就一定要写big-three,拷贝构造,拷贝赋值、析构函数

完美转发:
在这里插入图片描述
不完美的转发:
在这里插入图片描述

forward(2): 右值放进去之后就会变成左值,forward设计有问题。

forward(move(a)) 同上一样的错误;

forward(a): 左值根本就不能传入;

标准库的完美转发:
在这里插入图片描述
move之前就会调用forward,就是用了完美转发(也就是做了完美的转交);

22、move移动构造和move移动赋值

带move的class;

MyString类中有指针数据,就一定要写big-three,拷贝构造,拷贝赋值、析构函数

最终的目标,是生成一个类型,声称是可以被偷的,这样就可以对容器的性能有大幅度提升;

注意:

  • 拷贝构造和拷贝赋值都是在做拷贝;参数类型是引用;
  • 移动构造和移动赋值传入的参数是右值引用;
  • 新指针move原来指针后,原来的指针一定要赋值为NULL,不然会两个指针指向同一处,在析构函数时会出错,并且在析构函数汇总再将原来指针删除;

在这里插入图片描述在这里插入图片描述

各个容器的测试:

  • MyString:有move构造和赋值;
  • MyStrNoMove:没有move构造和赋值。
    在这里插入图片描述

Vector测试:
在这里插入图片描述

3000000个元素,为什么会调用构造函数7000000多次?

因为vector容器空间不够时,回进行两倍成长,会将原来的数据(拷贝/移动)到新的地方。
在这里插入图片描述

为什么三种方式的时间差距那么大?

M cl1(c1):来源端容器数据 的头尾,一个个拷到目的端去;

在这里插入图片描述

M c2(std::move(c1)): 将来源端当成右值,这样就可以偷了;(注意往下,c1是不可以在用了,它的资源已经被偷了)

cl1.swap(cl2): 只是交换指针,目的端和来源端指向同一块东西,再将初始的指针置为空,最后将它释放掉。
在这里插入图片描述

容器array

内部是一个c++数组,将他包装成c++的一个类;
在这里插入图片描述

没有构造函数,没有析构函数;
在这里插入图片描述

容器hashtable

篮子是vector,每个篮子下都有一个链表;

容器的元素大于 篮子的个数时,篮子的个数就增加两倍;

篮子扩容时,需要进行扩容;元素需要重新排序:rehashing;

hashcode;
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ThinkingF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值