C++11

目录

initializer_list

左值右值

什么是左值,什么是左值引用?

什么是右值,什么是右值引用?

完美转发

lambda表达式

捕捉变量

引用捕捉所有父作用域的变量

以值得方式捕捉所有父作用域的变量

可变模板参数


2011年标准委员会发布了C++11,其中更新了许多新东西,这里和大家一起分享学习其中较为重大的更新。

initializer_list

当我们实例化一个类的时候,通常会在初始化列表对其中的成员进行初始化,其成员个数也是我们需要在传参时进行匹配的个数,如果当我们传递的参数与构造函数的参数不匹配的时候它会报一个参数不匹配的错误。

实际是在构造时编译器会把{}识别成initializer_list,我们可以尝试打印看一下它是不是和我们说的一样。

可以看到,编译器确实会把{}识别成initializer_list。但是当我们调用vector的话同样也用{}进行初始化就没有问题。

那么我们去看看这个initializer_list到底是什么

原来他其中是又两个指针成员,一个是first,一个是last,当我们用{}对vector进行初始化时,实际是用initializer_list的first和last对其进行初始化。我们可以对其大小进行打印来进一步证实它是否真的是只有两个成员指针

左值右值

什么是左值,什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

左值没有什么过多好说的,我们长期能看见,例如当我们定义一个变量并对其赋值的时候此时的变量就是一个左值

什么是右值,什么是右值引用?

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址。右值引用就是对右值的引用,给右值取别名。

右值,分为两种,一种是内置类型的右值,也被称为纯右值。例如一个表达式 a=x+y,这里是x+y就是一个右值,我们不能对它进行取地址操作,它也不能出现在=符号的左边。

而自定义类型的右值也被称为将亡值。例如我们写下这样一段代码

按照以往我们所学的知识,在str被销毁的时候会对其值进行拷贝到一个临时对象里面,然后再由临时对象对ret进行构造,但是大多编译器都会进行优化,连续的构造或者拷贝构造,这里编译器会优化成在str销毁之后去直接构造ret。

但是在c++11之后,不再是拿返回值再去对其进行构造,如果这个自定义类型很小的话构造起来可能会很方便,但是如果这个自定义类型呢。所以在c++11Z之后,面对这种情况,编译器会认为此时的str是一个将亡值,反正你就要被销毁了,不如把你的资源全部转交给我,我替你延续下去。所以这里就会转变成,str在销毁之前会和ret交换资源,str会带走ret中的无效资源,并且继承str中的资源。

从图片中确实可以看到str指向的那块中间变成了ret指向的那块空间了。

完美转发

先看这样一段代码

根据左右值的区分,打印出来应该是 右值--左值--右值--左值--右值但是当我们真正打印出来hi后却发现和我们预期的答案完全不一致

可以看到全都是左值引用。问题就出在当我们把参数传递给perfectforward之后,这里的T模板虽然是一个万能模板,但是注意看下一句Fun函数的调用,根据前面说所,右值是没有地址的,如果t是一个右值,那么他又如何作为参数来进行传递,这里我们看到以为t是一个右值,但其实他的本质还是一个左值。所以打印出来的就全是左值,如果想要按照我们预期进行打印的话就可以加一个forward对其属性保持,使它右值就是右值,左值就是左值

lambda表达式

看这样一段代码,如果我们想要对水果名称,单价,和评价进行排序的话,就需要对其每一个需要排序的东西写一个仿函数,但是如果每一次比较逻辑不一样都要写一个仿函数的话会很复杂,因此在C++11中出现了lambda表达式。

lambda的语法是

1:[capture-list] : 捕捉列表,该列表总是出现在lambd
判断接下来的代码是否为lambda函数,捕捉列表能
函数使用。
2:(parameters):参数列表。与普通函数的参数列表一
连同()一起省略
3:mutable:默认情况下,lambda函数总是一个cons
性。使用该修饰符时,参数列表不可省略(即使参数为

4:->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回
值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
导。
5:{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获
到的变量。

我们先来简单看一下它的使用

比如我现在要交换两个值,在函数体的位置写上交换逻辑就可以。

同样,现在我们需要比较的时候就不需要对每一个不同的比较逻辑写一个仿函数。

[]一直说它是捕捉列表,到现在也没使用拿它到底有什么用我们继续往下看。

捕捉变量

比如我们要去商城买东西,但是我们有一张会员卡,所以每次消费的时候都需要打一个者,但是每次都需要输入这个折扣会显得太麻烦,所以我们可以捕捉一个固定值,让每次消费的价格去*这个折扣。

同样,前面说到参数部分也可以省略不写,但是没有参数我们的函数体就无法识别当前变量,此时捕捉列表也可以写一个&来表示以引用的方式来捕捉所有父作用域的变量,包括this指针,所以当我们捕捉之后想要用哪一个变量都是可以的。

引用捕捉所有父作用域的变量

以值得方式捕捉所有父作用域的变量

当然,如果我们想要捕捉的某些值是不希望它可以被改变的,我们就可以通过捕捉其值的方式,让它作为原值得拷贝传递过去。

同样,捕捉列表也是可以结合起来的。例如我想要某个值无法被修改,但是其他值可以被修改可以让其他值被引用捕捉,而固定的值按照值形式捕捉

越是花里胡哨的东西底层越是简单明了,例如范围for,看起来很神奇,但是它底层也只是简单替换成迭代器。而lambda同样,它的底层就是一个仿函数

可变模板参数

可变参数模板就是可以传递1-n个参数

例如,当我写下一个日期类,如果自己写过构造函数之后,那么我就需要传固定的参数个数给构造函数,但是中间添加一个可变参数模板,那么就会作为一个参数包传递给类。

我们在容器中都会看到这样一个接口,这里拿list来举例

假设现在list里面存放了一个pair,当我们使用匿名对象去构造一个list对象出来的时候,首先会去构造这个匿名对象,然后在用匿名对象来构造list对象,相当于会使用两次构造,但是如果使用emplace_back来构造,他会把构造的数据作为参数包往下一直传递,直接用当前参数来进行构造。

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值