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 ...
随机推荐
- rdesktop共享剪贴板的问题
使用-r clipboard:PRIMARYCLIPBOARD参数来共享剪贴板,连接到window7 但有时就不好用了,剪贴板没有同步 找到一些相关的资料 https://bugs.launchpad ...
- PowerShell Start 1 - 使用Get - Help.
详细帮助命令参见:https://msdn.microsoft.com/zh-cn/powershell/scripting/getting-started/fundamental/getting-d ...
- 雾里看花般的迷茫--货运APP
移动互联网正在改变我们的生活方式,各种手机APP充斥着人们的生活,物流行业也不例外.货运APP的出现,对于物流行业是一个提升的机会,也是迈向标准化和专业化的一个有效途径. 经过三十多年的发展,我国物流 ...
- 深度系统deepin使用初体验
最近使用linux系统比较多,因为很多项目要用到,而且厌烦了windows流氓软件各种广告的繁琐,因此决定在自己的本子上安装linux系统.然后了解到了deepin操作系统,竟然是武汉一个公司发行的版 ...
- 使用js倒计时还有几天及计时过了几天
公司主营业务是体育赛事为主的:尤其是足球赛事.现正值2016法国欧洲杯:之前忙于做欧洲杯专题没时间总结.现分享一个工作中需求带来的经历. 需求是:从现在开始倒计时欧洲杯还有多久开赛:当开赛之后开始计算 ...
- 自定义一个可复用的BaseAdapter
1.我们一点点开始改: 首先我们自定义BaseAdapter,等下我们就要对他进行升级改造 /** * Created by Jay on 2015/9/21 0021. */ public clas ...
- php常用数组函数回顾一
数组对于程序开发来说是一个必不可少的工具,我根据网上的常用数组函数,结合个人的使用情况,进行数组系列的总结复习.里面当然不只是数组的基本用法,还有相似函数的不同用法的简单实例,力求用最简单的实例,记住 ...
- Java 动态生成复杂 Word
Java 动态生成复杂 Word 阅读目录 1. 制作 Word 模版,将你需要动态生成的字段用${}替换.2. 将 Word文档保存为 xml .3. 引入项目. 项目中需要用 java 程序生成d ...
- android中的位置服务(LBS)
自己的位置:LocationManager 基本用法:创建实例:LocationManager locationManager = (LocationManager)getSystemService ...
- 【leetcode❤python】 278. First Bad Version
#-*- coding: UTF-8 -*-# The isBadVersion API is already defined for you.# @param version, an integer ...