C++11新特性
{}
c++11更新的首当其冲就是对初始化开刀,它觉得前面版本各种初始化太乱了,所以直接让{}可以初始化一切,并取名叫列表初始化,和类的初始化列表不一样嗷。
而这里比较出名的就是 initializer_list 它本身是一个类模版,会自动识别{}的列表给对象的操作,而一旦识别,它本质上就是个数组,里面两个指针,一个指向头,一个指向尾,然后一个个拷贝进去,所以它支持范围for。但是请注意区分。
右值引用
对于左值咱可熟悉的很,随便定个变量它的属性就是左值,那么右值和它有什么区别咧,简单理解,右值就是不能取地址的值。也正因为更新出来了右值,所以连构造都多了俩,移动构造,移动赋值。那么我们先来学习理解一下右值的特性和使用。
首先右值和左值不能直接互相引用,但是可以间接,比如说右值引用左值 需要使用move()函数,简单理解就是强转类型,而左值引用右值 这玩意不是日常操作嘛。最后,右值引用完之后,它的属性就变左值了,理解起来就是我们引用就是为了取到它,如果还是右值,那没地址咋取。
好了,大的要来了。
我们先看前两个,这就是个重载嘛,不就是分const和非const嘛,再结合一下我们上面说的右值是具有常性的,所以如果到此为止,左值会走第一个,而右值会找最匹配的,所以捏着鼻子就走第二个去了,但是一旦有了第三个,那它们就各走各的了。(&&就是右值引用的符号)
移动构造、移动赋值
这俩可以说是右值引用的精华所在了。
class A
{public:void swap(A& s){std::swap(_data, s._data);std::swap(_cap, s._cap);std::swap(_size, s._size);}A(string s,int x,int y):_data(s),_cap(x),_size(y){}A(const A& s):_data(nullptr){//拷贝构造 懒得写了 开空间尾插反正}private:string _data = nullptr;size_t _cap = 0;size_t _size = 0;
};
这是一个简单的类,模拟的是string,如果我们没学,这功能就算齐了,但是今天一切都不一样了,我的右值,不需要你再专门给他临时构造个对象出来再拷贝构造,我右值可以直接把资源转给你,甚至都不需要析构我,因为我是没有地址的。
这不比前面的快多了,而移动赋值也是同理,但是甚至都不用swap。
但是,我们还得注意一下这里的小坑,那就是如果我们右值传一次之后如果再传呢,咱肯定知道它是右值,但是编译器不知道,右值引用后属性变左值,再传不就变左值引用了。
所以祖师爷搞了一个引用折叠的概念出来,你可以理解为负负得正,只有传的数据是右值,且参数列表接收的也是右值,那才是右值,不然统统都是左值。
然后大的又来了,今天的大瓜一个接一个呢。
万能引用、完美转发
上面提到了,两个都是右值才是右值,传的数据我们无法决定,开始参数是我们决定的,那是不是以后不管咋样,我的参数都给&&,那是不是就做到左右值都能搞定并且只要写一个的情况呢,反正我是什么值,由使用者决定了。而折叠的问题祖师爷还给我们解决了 认识一下吧 forward。
它会保持你原本的属性,做到右值再次传入还是右值。
可变模版参数
对值的刀动完了,现在轮到模版了,这个可变模版参数我们可以理解成模版的模版。
传统的模版:有几个参数我就得定几个,要是有必要我template先写个100行再开始写函数。
现在:爷不写了,我给自个加个参数包,我管你上面类型,先传进来我再去实例化。
请注意,这里的Args它不是个容器捏,所以如果我们想知道这个包里有多少种类型还不能用for来遍历呢,虽然我也觉得for挺合理的,但是确实不行,那该咋办呢,有点麻烦。
实际上这种方法就是利用递归的思想,每次剥一层出来,是不是感觉很low,别急还有一种相对不那么low的。
这个呢是利用包扩展要扩展完,但是我们的参数只给了一个,所以它不得不继续一个个展开一个个传,实际上和上面差不多。不过实际使用中,我们一般做不到这种情况,一般当个甩手掌柜把包一传,剩下的就交给编译器了,让编译器自个解析包里的内容。反正它也具备了模版的自动推导的特性。
lambda
这个其实就是c++从别的编译器里借鉴来的,对于某些情况想要简单的写函数,或者说不想看一点代码还得往上一点点翻以及不想写注释的兄弟来说,这就是福音了,它的使用很简单,同时也很易懂。
首先就是一个捕捉列表,这个即便是空着也不能不写,理论上它可以自动帮你捕捉所有这个作用域里的对象,但是实际上它没那么神,它会看看后面你用到哪些,就帮你捕捉哪些。
然后就是函数基本的参数列表,这个就可以省略了,毕竟确实有不用参数的函数,它后面可以跟返回类型,也可以不跟,它会自动推导。
最后,是函数体的实现部分,这里也是即便为空也不能省略。
以上部分写完,一个lambda就出炉了,但是我们看到返回类型部分的规则就知道它的类型不太好确认,所以使用auto接收,同时它也没有函数名,这是由系统用个uuid随机给它生成出来的。
lambda强大的地方除了大伙比较容易注意到的简便易懂,还有一个就是它的捕捉列表,祖师爷给它提供了非常多的选择。
首先如果给一个 = 那它就默认捕捉所有变量,以const形式捕捉,而给 & 则是捕捉所有变量,以引用方式捕捉,但是总会有兄弟想问,我不加&还要引用行不行,行。 在参数列表后面+mutable就行,但是这样等于是在lambda里创建了个独立的临时变量,是无法影响到外部的。
另外,上面的 = 和 & 还能一起用,比如我全部都想const 除了个别想引用,那就可以 , 分割结合。
c++11的STL多了几个容器,除了之前的initializer_list,还有array forward_list以及之前的unordermap和set。
而unordermap和set是最核心的容器变化 push/insert/emplace 则是最核心的接口变化,因为多了右值引用 所以都更新了。
还有之前提过的initializer_list版本的构造。
包装器、绑定
说包装器之前咱先来看。
这俩加上刚刚提的lambda.,就构成了咱调用的三大法宝,可是有时候咱可能混着写,可是调用可不能一个样的调,所以祖师爷搞了个包装器出来,它让我们可以以不同的方式写,但是以相同的方式调,底层也是仿函数,它的最大作用就是类型多样时的统一,不过用之前得先包个头文件:<functional>。
然后以function<>开头,<>中间写返回类型和参数类型就好,不过需要注意,但凡有一个对不上就会失败,包括个数都要完美匹配才行。
而对类,它还有更精密的区分,对于静态的,它需要取地址和指定类域,非静态的还得再考虑this指针,这就导致会多参数,这要是不说就是个陷阱了。详情看上面的avg函数。
绑定
这个东西怎么说呢,有两个功能,一个挺有用,另一个可以娱乐娱乐。
我们可以把它看成一个函数模版,或者说函数适配器,总之走到底层都是仿函数,它可以用来调整参数顺序和个数。
它引入了占位符的概念,用_1_2....来占位,始终代表第一个第二个...参数。
我们可以用auto或者上面的funtion来作为它的类型。我们直接看第一种使用叭。
即使交换顺序,它也是把第一个参数给_1第二个给_2。
它的另一个使用场景才是我们常用的。
改变参数个数,对于某些固定的参数,它可以直接给,然后你就可以少传了,就比如我们刚才function里提到的this,我们可以直接固定它,只传后面的参数。
以上就是c++11更新后的一些比较实用的内容了。