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 ...
随机推荐
- docker 介绍
docker 介绍 安装 sudo apt-get install docker.io sudo docker info 查看是否安装成功 hello world sodu docker run he ...
- PHP就业班心得:IP与域名以及DNS和端口号的概念
什么是IP地址 概念:IP地址就相当于人们的身份证号码!每一个连入Internet的计算机都应该有全世界独一无二的IP地址 IP地址是使用32个bit位来保存,也就是4个字节! 为了方便记忆,采用十进 ...
- 关于strlen误用的一点记录
今天帮一个朋友查一个错误,是运行时报vector iterator incompatible,一般这种问题是向量和迭代器的类型不兼容,或者是进行迭代器判等时前后向量的结构发生变化,如erase操作之后 ...
- T-SQL存储过程、游标
Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø ...
- 谈谈html5存储之IndexdDB
IndexdDB简介 html5中indexdDB是一种能在浏览器持久的存储结构化数据的数据库:且具有丰富的查询能力. 新建一个IndexdDB数据库 IDBOpenDBRequest定义有几个重要的 ...
- HTML纯javaScript代码写图片轮播
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- PHP之初识PHP(1)
酸菜记 之 初识PHP 这是我从零基础了解PHP开始学习的笔记,共同学习,有不对之处,望指出共同学习. 一.初了解PHP PHP 是超文本预处理语言 php HyperText Preprocesso ...
- AngularJS 后台交互
我们可以使用内置的$http服务直接同外部进行通信.$http服务只是简单的封装了浏览器原生的XMLHttpRequest对象. 1.链式调用 $http服务是只能接受一个参数的函数,这个参数是一个对 ...
- js中arguments的用法
了解这个对象之前先来认识一下javascript的一些功能: 其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载.Javascrip中国每个函数都会有一个Argume ...
- 一些Unity基础操作的性能测试
从以前一个文章转移过来的内容,以后会进一步进行测试 内容 毫秒数(Editor) 毫秒数(Build PC) 加减内部变量 4ms 1ms new List<int>() 559m ...