对于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),程序的其它部分不会有任何察觉。为了实现这样的操作我们需要做以下两件事情:

  1. 对string实现一个移动构造函数、移动赋值函数。这些函数对内部指针进行swap操作,而不是copy操作。
  2. 通过std::move来强化转化成右值引用,用以触发移动赋值函数。编译器正是通过参数类型是T&&,才知道应该使用移动版本的operator=()而不是copy版本的operator=()。
 new_stri = std::move(old_str);

要对old_str转化为右值引用是因为它并不是真正的右值,它不是一个临时变量。但因为它即将被销毁,所以效果等同于一个临时变量。因此可以安全的转换,从而调用移动赋值函数并悄悄的"移动"它的内存资源。

推荐阅读:

  1. 知乎:如何理解C++中的move语义?邱昊宇的回答

参考资料:

  1. Visual C++ Team Blog:Rvalue References: C++0x Features in VC10, Part 2

c++11的右值引用、移动语义的更多相关文章

  1. C++11之右值引用(二):右值引用与移动语义

    上节我们提出了右值引用,可以用来区分右值,那么这有什么用处?   问题来源   我们先看一个C++中被人诟病已久的问题: 我把某文件的内容读取到vector中,用函数如何封装? 大部分人的做法是: v ...

  2. c++11之右值引用

    本文大部分来自这里,并不是完全着行翻译,如有不明白的地方请参考原文. 在c++中,创建临时对象的开销对程序的影响一直很大,比如以下这个例子: String getName(){ return “Kia ...

  3. C++11 的右值引用

    作者:Tinro链接:https://www.zhihu.com/question/22111546/answer/30801982来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  4. C++11之右值引用(一):从左值右值到右值引用

    C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是 ...

  5. 【C/C++开发】C++11:右值引用和转发型引用

    右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如 ...

  6. C++ 11的右值引用

    目录 一.问题导入 二.右值和右值引用 2.1 左值(lvalue)和右值(rvalue) 2.2 左值引用和右值引用 总结 参考资料 C++11 引入了 std::move 语义.右值引用.移动构造 ...

  7. C++11特性-右值引用

    什么是左值,什么是右值 常见的误区有 = 左边的是左值,右边的是右值. 左值:具有存储性质的对象,即lvalue对象,是指要实际占用内存空间.有内存地址的那些实体对象,例如:变量(variables) ...

  8. [转载]如何在C++03中模拟C++11的右值引用std::move特性

    本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalu ...

  9. C++11之右值引用(三):使用C++11编写string类以及“异常安全”的=运算符

    前面两节,说明了右值引用和它的作用.下面通过一个string类的编写,来说明右值引用的使用. 相对于C++98,主要是多了移动构造函数和移动赋值运算符. 先给出一个简要的声明: class Strin ...

随机推荐

  1. Steve Loughran:Why not raid 0,its about time and snowflakes!!!

    与RAID-0阵列的同组管理相比,Hadoop更喜欢一组单独磁盘.在Hadoop集群中,读取速度是最能体现性能的重要指标.在Steve Loughran文章中,尤其强调了这一点,他还指出,由于驱动器速 ...

  2. javascript 使用方法名作为参数

    Example <!DOCTYPE html> <html lang="en"> <head> <meta charset="u ...

  3. java中jdk和jre的区别

    JRE: Java Runtime Environment JDK:Java Development Kit JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库.是使用java ...

  4. 【转】不容忽视的ClassNotFoundException

    转载地址:http://it.deepinmind.com/jvm/2014/04/11/classnotfoundexception-is-it-slowing-down-your-jvm.html ...

  5. linux两台服务器之间文件/文件夹拷贝

    跨服务器拷贝需要用到的命令是scp. ----------------------拷贝文件夹---------------------------------------------- 把当前文件夹t ...

  6. 【Splay】bzoj3223-Tyvj1729文艺平衡树

    一.题目 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 ...

  7. apache开启.htaccess及.htaccess的使用方法(转)

    apache开启.htaccess及.htaccess的使用方法 作者: 字体:[增加 减小] 类型:转载 时间:2010-12-02 今天本地调试PHP程序,用到了.htaccess,而默认配置里面 ...

  8. PHP性状的使用

    <?php trait Geocodable{ /** @var string */ protected $address; /** @var \Geocoder\Geocoder */ pro ...

  9. ubuntu 14.04 vsftpd安装问题

    sudo apt-get install vsftpd; 打开允许访问用户名 local_enable=YES write_enable=YES chroot_list_file=/etc/vsftp ...

  10. contos7 安装netcore 和vscode

    安装netcore 参考 http://www.cnblogs.com/hohoa/p/5691071.html 1 在root下新建一个文件夹"dotnet"用来放SDK,然后用 ...