C++11右值引用以及移动语义

本文旨在讲解C++中的右值引用,以及移动语义!在讲解之前,我们首先要讲解一下什么是左值以及右值,又该如何区分二者呢?

左值

所谓的左值,我们通常的认为只能存在赋值运算符的左侧,然而事实上并不是这样的,左值也可以位于赋值运算符的右侧!那么我们又如何辨别一个类型是左值还是右值呢?我们对于左值一般有着以下的定义:如果该值可以进行取其的地址,那么我们就称该值为左值,下面简单的列举几个左值的例子!

其中,a,b变量都是左值,我们可以取出他们的地址.。

右值

既然有了左值的定义,那么我们就可以得出不能对其取地址的那就称其为右值!右值只能出现在赋值符号的右边,不能出现在赋值符号的左边,右值不能取地址。右值一般包括以下几大类:字面常量,表达式,函数返回值。 

左值引用与右值引用比较

有了左值和右值的定义了,我们来看一看左值引用以及右值引用的作用吧。

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。引用都可以减少我们对数据的拷贝。

1.左值引用总结:

1.左值引用只能引用左值,不能引用右值。

2.但是const左值引用既可引用左值,也可以引用右值!

2.右值引用总结:

1. 右值引用只能右值,不能引用左值。

2. 但是右值引用可以move以后的左值。

那么既然有了左值引用,为什么还要引进右值引用呢?下面就先来看一下左值引用的作用吧。

3.左值引用的使用场景:

做参数和做返回值都可以提高效率

4.左值引用的短板:(引入右值引用)

但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。例如:bit::string to_string(int value)函数中可以看到,这里只能使用传值返回,传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。

对于上述情况,我们不能使用左值引用来进行返回,因为其其中的变量是一个局部变量,出了作用域之后就会销毁。对此,我们可以使用右值引用和移动语义来解决问题!

对于我们的右值,我们又分为两大类:将亡值和纯右值。

将亡值:自定义的右值。

纯右值:内置类型右值。

对于上述情况,我们的str是一种将亡值,我们能不能不要进行深拷贝而是选择进行对他的资源进行一种转移,窃取呢?这时就引进了我们的右值引用和移动语义了!

5.移动构造

我们只需要在bit::string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占为已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

所谓的移动构造就是将资源进行转移,但是我们需要用右值引用进行传参来实现移动构造。 编译器会根据最合适的情况,调用相应的函数,如果返回值是一个将亡值,那么编译器会优先调用移动构造!


下面看一下移动构造和拷贝构造的区别

6.移动赋值 

不仅有移动构造,我们还有移动赋值

这里运行后,我们看到调用了一次移动构造和一次移动赋值。因为如果是用一个已经存在的对象接收,编译器就没办法优化了。bit::to_string函数中会先用str生成构造生成一个临时对象,但是我们可以看到,编译器很聪明的在这里把str识别成了右值,调用了移动构造。然后在把这个临时对象做为bit::to_string函数调用的返回值赋值给ret1,这里调用的移动赋值。

本质移动构造和移动赋值都是进行了资源的转移,而不是像构造函数和赋值函数一样进行了深拷贝!所以节省了大量的时间,这也是引进右值引用的原因!

那么当同时存在移动构造和拷贝构造的时候,编译器该如何选择呢?这个问题很简单,我们的编译器一般会选择与其最匹配的函数以及模版,对于一个将亡值来言,编译器认为他既然都要离开了,并且还有其他人想要他的资源,那么编译器我就不再进行深拷贝了,我直接将这个将亡值的资源转移给你即可,这样编译器也可减少了深拷贝的次数,也提高了编译器的效率!


后续:

下面再来补充一些关于移动语义和右值语义的相关的知识:

1.对于move函数而言,我们知道其可以将一个左值转化为右值,但是其真正的实质上只是将其返回值转化为右值,并没有将其本身转化为右值属性!!


2.C++11引出右值语义之后,在很多场景下也使用了右值语义,比如我们的swap函数!

对于swap函数,在C++98的时候,我们默认只能使用深拷贝的方式进行交换,但是C++11之后,我们可以使用右值引用来进行移动交换!这样可以减少深拷贝的代价!


3.C++11后,大多STL容器,基本也都增加了移动构造/移动赋值! 

例如:vector中实现了移动语义!


还有string类也完成实现了移动语义!


4.所有的插入接口也引进了右值引用的版本!


分析:问什么右值引用引用之后其属性就变成了左值呢?

当我们进行右值引用的时候,我们想要做的就是对其引用的对象资源进行转移,但是右值是不能进行修改的,所以编译器将其强制转化为左值,因为我们的移动构造/赋值函数都是需要进行swap交换资源的,如果右值引用后仍然是右值,那么毫无疑问,我们的移动构造/赋值函数就无法完成!

至此,已经简单得介绍了左右值以及其引用的定义,以及移动构造和移动赋值的定义。对于右值语义的引进,对我们的深拷贝的类具有了很大的意义!到这里,就基本简单介绍完毕了,希望读完本文,能对读者有一定的收获!

  • 43
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值