c++11的右值引用、移动语义
对于c++11来说移动语义是一个重要的概念,一直以来我对这个概念都似懂非懂。最近翻翻资料感觉突然开窍,因此记下。其实搞懂之后就会发现这个概念很简单,并无什么高深的地方。
先说说右值引用。右值一般指的是表示式中的临时变量,在c++中临时变量在表达式结束后就被销毁了,之后程序就无法再引用这个变量了。但是c++11提供了一个方法,让我们可以引用这个临时变量。这个方法就是所谓的右值引用。
那么右值引用有什么用呢?避免内存copy!
不同于其它语言,在c++里变量是值语义(在JAVA、Python变量是引用语义)。因此对于赋值操作意味着内存拷贝而不是简单的赋值指针。而右值引用的一个作用就是我们可以通过重新利用临时变量(右值)来避免无意义的内存copy。
看这个例子(取自Rvalue References: C++0x Features in VC10, Part 2)
string s0(“my mother told me that”);
string s1(“cute”);
string s2(“fluffy”);
string s3(“kittens”);
string s4(“are an essential part of a healthy diet”); string dest = s0 + ” ” + s1 + ” ” + s2 + ” ” + s3 + ” ” + s4;
第7行对string求和中,每次调用operator+()都会创建一个临时变量,一共创建了8个临时的string对象。而这里真正低效的原因是,每次创建一个string对象都需要从堆上申请空间,将字符串的内容copy进来,并且这些临时的string都是只用一次。
显然这是低效的,为什么不这样子做呢:假设s0+""的时候创建的临时变量是temp_str,此后我们一直是用这个临时变量而不是重写创建。这样做表达式的结果是不会有任何改变,并且因为在opertor+()创建的都是程序其它地方不会引用到的临时变量,所以这样做也不会有任何副作用。相当于我们偷偷的做了个其它人无法察觉的小优化。
要如何做才能引用这个临时的string对象呢?答案就是右值引用。通过右值引用我们就能重新利用表达式中的临时变量,并且以更高效的指针swap来避免内存copy。
需要说明的一点是,右值引用优化的是避免对象在堆空间的内存的copy。在堆上的内存我们可以简单的通过指针交换来传递内存资源的所有权(类似于vector的swap方法),而对于栈上的内存不可避免还是需要copy。举一个例子这里移动对象有点像放风筝:我放风筝,放着放着觉得累了就交给你,具体是怎么交法呢?你先制作一个和我手上拿的一模一样的手柄,接着我再把线剪短递给你,你把线绑在你的手柄上,交接完毕!手柄对应是栈空间、风筝对应是堆空间。
这种技术十分有用,不仅仅是在处理临时变量的时候起作用,有的时候我们想要使用这个转移资源(内存)的效果时,也可以强制将类型转为右值引用(std::move)来触发对象移动。
举一个例子,比如说对于vector的动态扩容。熟悉vector的实现的都知道,在对一个vector进行push_back时有可能会触发内存的重新分配,这个时候需要把原来内存的对象copy到新分配的内存上,最后再释放原来的内存。假设这个vector里面存放的是string对象,那么我们在执行简单的对象赋值(调用的是string::operator=()方法)的过程中,我们copy的不仅仅是sizeof(string)的内存,我们还copy这个string内部指针指向堆空间上的内存。通过观察可以发现,其实我们完全不必去拷贝内部指针指的那部分内存,因为原来的string对象在赋值完后就要被销毁,如果我们将这个指针偷偷的拿过来(swap),程序的其它部分不会有任何察觉。为了实现这样的操作我们需要做以下两件事情:
- 对string实现一个移动构造函数、移动赋值函数。这些函数对内部指针进行swap操作,而不是copy操作。
- 通过std::move来强化转化成右值引用,用以触发移动赋值函数。编译器正是通过参数类型是T&&,才知道应该使用移动版本的operator=()而不是copy版本的operator=()。
new_stri = std::move(old_str);
要对old_str转化为右值引用是因为它并不是真正的右值,它不是一个临时变量。但因为它即将被销毁,所以效果等同于一个临时变量。因此可以安全的转换,从而调用移动赋值函数并悄悄的"移动"它的内存资源。
推荐阅读:
参考资料:
c++11的右值引用、移动语义的更多相关文章
- C++11之右值引用(二):右值引用与移动语义
上节我们提出了右值引用,可以用来区分右值,那么这有什么用处? 问题来源 我们先看一个C++中被人诟病已久的问题: 我把某文件的内容读取到vector中,用函数如何封装? 大部分人的做法是: v ...
- c++11之右值引用
本文大部分来自这里,并不是完全着行翻译,如有不明白的地方请参考原文. 在c++中,创建临时对象的开销对程序的影响一直很大,比如以下这个例子: String getName(){ return “Kia ...
- C++11 的右值引用
作者:Tinro链接:https://www.zhihu.com/question/22111546/answer/30801982来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...
- C++11之右值引用(一):从左值右值到右值引用
C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是 ...
- 【C/C++开发】C++11:右值引用和转发型引用
右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如 ...
- C++ 11的右值引用
目录 一.问题导入 二.右值和右值引用 2.1 左值(lvalue)和右值(rvalue) 2.2 左值引用和右值引用 总结 参考资料 C++11 引入了 std::move 语义.右值引用.移动构造 ...
- C++11特性-右值引用
什么是左值,什么是右值 常见的误区有 = 左边的是左值,右边的是右值. 左值:具有存储性质的对象,即lvalue对象,是指要实际占用内存空间.有内存地址的那些实体对象,例如:变量(variables) ...
- [转载]如何在C++03中模拟C++11的右值引用std::move特性
本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalu ...
- C++11之右值引用(三):使用C++11编写string类以及“异常安全”的=运算符
前面两节,说明了右值引用和它的作用.下面通过一个string类的编写,来说明右值引用的使用. 相对于C++98,主要是多了移动构造函数和移动赋值运算符. 先给出一个简要的声明: class Strin ...
随机推荐
- python中的argparse模块学习
该模块是python用于解析命令行和参数的标准模块 好文推荐:http://blog.ixxoo.me/argparse.html,不仅域名个性,文章翻译的也很好.推荐
- 关于xfce桌面程序启动失败
当双击桌面图标的时候,出现如下错误信息:Process org.xfce.FileManager exited with status 1 于是做出如下尝试: 1. ps aux | grep Fil ...
- 【原创】如何在Android Studio下调试原生安卓Framework层面的源代码
1. Open Existing Android Studio Project. 2. 打开后, Projects -> Android 里面是空的. 这时候,需要选到 Projects-> ...
- python装饰器通俗易懂的解释!
1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...
- Salesforce 执行顺序
在服务器上,Salesforce按以下顺序执行: 1,从数据库加载原始记录或初始化一个用于更新插入(upsert)语句的记录. 2,从请求加载新记录的字段值并覆盖旧的值. 如果请求来自一个标准的UI( ...
- js高级程序设计书中,有一句话在全局作用域中定义的函数实际上只 能被某个对象调用???
js没有块级作用域(题外话:函数可以作为一个块级),所以我们经常使用闭包来模拟块级作用域,以避免变量或者函数因为名称相同而产生的冲突. 重点来了: 所以,如果我们把哪个变量或者函数放在全局作用域中,那 ...
- easyui DataGrid 工具类之 util js
var jq; var tab; var tabsIndex; /** ...
- Oracle数据库基础
Oracle基础知识 Oracle的主要特点 1.支持多用户.大事务量的事务处理 2.在保持数据安全性和完整性方面性能的优越 3.支持分布式数据处理.将分布在不同物理位置的数据库用通信网络连接起来,在 ...
- 使用FIR.im发布自己的移动端APP
一次很偶然的机会知道FIR.im,这家公司主要的产品就是帮助开发者方便便捷地发布iOS或者Android应用的.今天我就聊聊如何在FIR.im中发布自己的APP,然后让加入UUID的设备通过网页直接下 ...
- angular的$scope
angularJS是一个MVVM的前端js框架. $scope的作用是angular向视图(html)传递数据的通道,它不负责处理和操作数据.也就是说要想向视图传递数据的话,必须定义$scope变量. ...