再谈右值引用与移动语义


之前在总结C++11新特性的时候,写过一篇文章,专门说到了右值引用、移动语义以及从中牵扯出来的引用折叠和完美转发等概念《C++11新特性之右值引用与移动》

那篇文章例子很多,仔细阅读后也可以完全理解这几个概念。但是今天我想重新组织下语言,就像面试中被问到“你怎么看待C++中的右值引用和移动语义”,进行一次小总结。当然,如果你最近也正好需要面试,下面的文字可能能帮你梳理一遍思路。

右值引用


右值引用就是对右值的引用(废话),而右值是相对于左值而言的:左值持久,右值短暂。

右值这个概念在C++11之前就已经存在了,比如你写一个 ```5*j=6``` 这样的表达式,就会被编译器提示,不能给右值赋值。

右值引用,是一种类型。类型可以用来定义变量,用右值引用这个类型来定义的变量,是一个左值。这一点很绕,也是完美转发比较难理解的地方。

std::move


std::move虽然叫move,但是它并没有实现任何移动的功能(也就是没有实现移动语义)。

那std::move是干嘛的呢?其实它只是做了一个类型转换:传入一个左值变量,返回其右值引用。

对,就是这么简单。

移动语义


右值引用和左值引用是两种不同的类型,既然是不同的类型,那用作函数参数时,就可以实现重载:由具体传入的参数类型来决定调用哪个版本的函数。

如果是右值引用为参数的版本,我们说其拥有移动语义,即,这个函数被批准窃取参数的内部资源,而不用做深度拷贝,这样效率较高。但是,移动语义有个重要的限制:

被用作参数的右值引用所指向(绑定)的对象,在移动语义完成之后,内部资源已被“窃取”,不能再被其他地方使用;其只是维持一个可析构可销毁的状态。  

移动之后的变量可析构可销毁,由移动语义的实现方保证;移动之后不被其他地方使用,则由移动语义的调用方保证。

所以,说白了,移动语义,就是在C++添加了右值引用这一类型之后,对原有的函数(左值引用,拷贝语义),增加了一个重载的版本。这个版本做出了一些限制(参数调用后可析构可销毁,不可再次使用),也带来了效率提升(比如拷贝指针而非深度拷贝内存块)。而要调用到这个新的重载版本呢,就需要用std::move强制的获得一个右值引用类型变量。

总结


其实就这么点东西。引用折叠和完美转发找时间再说,稍微有点麻烦,而且用到的地方也更少一些。

目前标准库以及protobuf中的类型都已实现了移动语义,基本可以放心使用。如果是自定义的类型,想要使用移动语义,就需要自己去重载右值引用参数版本的带有移动语义的函数了。重载时,要记得需要保证右值引用移动后的可析构可销毁状态~



推荐阅读:
C++11新特性之右值引用与移动
不要拷贝
关于内存泄露的一场虚惊

转载请注明出处: http://blog.guoyb.com/2018/07/07/move-again/

欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考:
后端技术小黑屋

Comments