简介

关于C++中的右值引用的详细可以看这一批博文《从4行代码看右值引用》。那一篇博文详细结合四行简单的代码详细介绍了右值引用的使用方法和一些场景,非常实用。

而本篇博文主要介绍一下我在学习右值引用的一些心得。因为在学习右值引用的时候,有一些地方非常难理解。所以写下这一篇博文,防止遗忘,由于对于C++涉猎不多,所以有一些不正确的地方,欢迎斧正。

为什么要使用右值引用?

对于普通开发者来说,右值引用的好处在于可以将保证在取用函数返回值的时候,减少一次复制的机会。如:

#include <iostream>
using namespace std; class Test
{
private:
public:
Test(/* args */) {
cout << "构造" << endl;
}
int a = 0;
~Test() {
cout << "析构" << endl;
} Test(const Test &a) {
cout << "复制" << endl;
}
}; Test GetTest() {
Test af;
return af;
} int main() {
Test a = GetTest();
return 0;
}

使用编译选项-fno-elide-constructors,可以得到结果

构造
复制
析构
复制
析构
析构

可以发现,为了保证正常运行和堆栈正常,程序对Test对象经过了一次构造和两次复制。分别是

  1. GetTest()函数中Test af的构造,由于af是GetTest()的临时变量,因此此时使用的内存空间是GetTest()里面的栈1
  2. 为了把af从GetTest()的栈中取出来,于是将把af指向的Test对象复制到了main()的栈,由于程序“并不知道”接下来的操作,所以此时对象的存在就是一个临时的。语句执行完,它就析构了。
  3. 为了保存这一个对象,我们使用了a,而为了将上一步得到的值复制到a,程序又进行了一次复制。

以上便是程序的全过程。而可以很清楚的发现,第2次复制得到的值和第3次复制得到的值,它们都位于main()的堆栈空间内的,他们的生命周期都是一样的,和main()一样长。那么我们有没有办法可以减少第3次的复制呢?

在没有右值引用的时候有两种方法。

  1. 靠编译器优化2
  2. 使用const Test&,常量引用。

第一种方法不表,以目前的编译器优化,的确可以获得非常多的优惠,但是这些编译器提供的,并不是语法提供的,不同的编译器可能存在不一样的表现。第二种在需要改变返回值的时候非常麻烦。

而右值引用可以解决这一问题。我们将main()函数的代码改为

int main() {
Test &&a = GetTest();
return 0;
}

编译运行后得到额结果为

构造
复制
析构
析构

可以看到,程序减少了一次复制。这一次的减少就是上述的第3次的复制。而且此时的变量a可以随意的更改。

那我们可以把第二次的复制也去掉吗?当然可以,比如把GetTest()的返回值改为Test&&,然后给af加上std::move强制转换成右值。但是,这样的操作破坏了程序的堆栈,会出现很大的问题。

右值引用和unique_ptr

unique_ptr是C++中智能指针的一部分,另外还有auto_ptrshared_ptr。但是刚刚使用C++不久,所以只对shared_ptr有一定的了解,而auto_ptr由于一些原因已经被弃用了。unique_ptr只允许一个智能指针对象拥有指针。而shared_ptr则允许多个,其内部有一个引用计数器,当引用为0时便释放空间,因此相对于unique_ptr,其所占的内存空间较大,使用的时候,效率也相对较低。

unique_ptr的实现必须保证不共享其指针,不复制到其他的unique_ptr,也不能使用值传递到函数里面。要将其所有权交给其他变量,只能通过“移动”的操作。

为了保证移动的语义上的正确性,被移动的unique_ptr必须为右值。因为右值是被认为是即将消亡的值(将亡值),所以移动操作可以把被移动的unique_ptr的引用值为nullptr。

std::move和std::forward

std::move是将一个左值转换为右值,在内部实现直接使用了强制转换static_cast<T&&>()。它一般用于将左值转换为右值,在unique_ptr转移的时候需要使用。一般来说,使用了move后,该对象就不应该继续使用了。

std::forward即完美转发,保证参数在传递过程中属性不变,右值还是右值,左值还是左值。

附录

  1. 此处表达可能有一些不正确,因为所有的函数调用及其局部变量应该都是在一个栈上。但是在函数结束的时候,必须要恢复栈的指针。这一步便需要把其局部变量全部删除,因此可以认为其局部变量是位于函数的栈中。
  2. 去掉编译选项-fno-elide-constructors

    ------------恢复内容结束------------

关于C++的右值引用的一些看法的更多相关文章

  1. C++右值引用浅析

    一直想试着把自己理解和学习到的右值引用相关的技术细节整理并分享出来,希望能够对感兴趣的朋友提供帮助. 右值引用是C++11标准中新增的一个特性.右值引用允许程序员可以忽略逻辑上不需要的拷贝:而且还可以 ...

  2. C++ 11 中的右值引用

    C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream>    #include &l ...

  3. 图说函数模板右值引用参数(T&&)类型推导规则(C++11)

    见下图: 规律总结: 只要我们传递一个基本类型是A④的左值,那么,传递后,T的类型就是A&,形参在函数体中的类型就是A&. 只要我们传递一个基本类型是A的右值,那么,传递后,T的类型就 ...

  4. c++11的右值引用、移动语义

    对于c++11来说移动语义是一个重要的概念,一直以来我对这个概念都似懂非懂.最近翻翻资料感觉突然开窍,因此记下.其实搞懂之后就会发现这个概念很简单,并无什么高深的地方. 先说说右值引用.右值一般指的是 ...

  5. VS2012 error C2664: “std::make_pair”:无法将左值绑定到右值引用

    在vs2012(c++)make_pair()改动: C++: template <class T1, class T2> pair<V1, V2> make_pair(T1& ...

  6. 右值引用、move与move constructor

    http://blog.chinaunix.net/uid-20726254-id-3486721.htm 这个绝对是新增的top特性,篇幅非常多.看着就有点费劲,总结更费劲. 原来的标准当中,参数与 ...

  7. 【转】C++11 标准新特性: 右值引用与转移语义

    VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...

  8. move语义和右值引用

    C++11支持move语义,用以避免非必要拷贝和临时对象. 具体内容见收藏中的“C++右值引用” .

  9. [转载] C++11中的右值引用

    C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...

随机推荐

  1. IntelliJ IDEA下git版本回退,版本还原

    原文链接:https://blog.csdn.net/hehyyoulan/article/details/80005272

  2. JDBC | 第七章: JDBC数据库连接池使用

    概述 数据库连接池是负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个.那么其中的运行机制又是怎样的呢?今天主要介绍一下数据库连接池原理和常用的连接池. ...

  3. Unity内生成深度贴图

    https://qiita.com/bokkuri_orz/items/08cbaeae6a34fed7f903 https://www.cnblogs.com/sifenkesi/p/4721649 ...

  4. html中实现倒计时功能(setInterval,clearInterval)

    倒计时主要用到的知识点:1.设置时间间隔的setInterval可以被clearInterval取消 2.毫秒转换为时分格式 这个是效果图 下面是js中的函数 var shijian=3600; va ...

  5. 【平台开发】— 4.mysql建库建表

    本想着把前端脚手架run起来了,然后就可以借着登录来捋一下前后端交互的过程.但是后端导入JPA的时候就发现了,还没有数据库. 既然是本着学习的目的,那咱也不想只在后端写死返回的数据,要做就做全套. 一 ...

  6. Palindrome subsequence(区间dp+容斥)

    In mathematics, a subsequence is a sequence that can be derived from another sequence by deleting so ...

  7. 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP

    洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...

  8. vue相关知识点及面试

    ### vue #### vue生命周期 beforeCreated `实例初始化,数据观察和event/watch事件配置之前被调用` created `实例创建后立即调用,数据观测,数据和方法运算 ...

  9. Solr专题(一)手把手教你搭建Solr服务

    一.Solr是什么,能解决什么问题? Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器.同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置.可扩展并对 ...

  10. js-正则表达式的初步应用(一)

    一.正则表达式是使用单个字符串来描述.匹配一系列符合某个句法规则的字符串搜索模式.注:搜索模式也可用于文本替换 例子1 输出结果  注:(我为了方便在控制台输出,所以结果如下) 例子2 输出结果 上面 ...