目录

前言

一.右值引用的概念

1.1 左值和右值的概念

1.2 引用和右值引用比较

二.右值引用的作用

2.1引用的缺陷

2.1 移动语义

2.2 右值引用的具体应用

2.3 对比引用总结

三.右值引用引用左值(move)

四.完美转化


前言

在C++98中也有一个引用,为左值引用,但是左值引用有一些不足。而C++11的右值引用的提出弥补了左值引用的不足。

左值引用和右值引用都是别名。

说明:下面说引用都代表C++98的左值引用。

一.右值引用的概念

右值引用也是一块空间的别名。只能对右值进行引用。使用是在类型后面加两个&&。

#include<iostream>
using namespace std;int Add(int x, int y){return x + y;
}int main(){const int&& ra = 10;//右值引用//函数返回值为一个临时变量,是右值int&& ret = Add(2, 3);//右值引用return 0;
}

1.1 左值和右值的概念

左值和右值是C语言的概念,但是C语言没有给出严格的标准,一般认为,可以放在等号"="左边的是左值,可以放在等号右边的是右值。但是这个说法是错误的。

比如:

#include<iostream>
using namespace std;int main(){//a,b是左值,10和20是右值int a = 10;int b = 20;//此时b也可以放在等号右边//a也可以放在等号右边a = b;b = a;return 0;
}

左值和右值是不好区分的,这里我们一般这样认为:

  • 左值:一般是可以修改的值,可以取地址的,通常是变量。
  • 右值:一般是常量(除const 修饰的),表达式或者函数的传值返回(生成临时变量)。

注意:传引用返回是右值。

C++11有对右值进行了严格的区分:

  • 纯右值:比如常量,表达式值a+b
  • 将亡值:比如函数传值返回,表达式的中间结果。顾名思义,将亡值的空间马上就要被释放了。

1.2 引用和右值引用比较

  • 引用,只能引用左值,不能引用右值。但是const引用既可以引用左值,又可以引用右值。
int main(){//a为左值,10为右值int a = 10;int& ra1 = a;const int& ra2 = a;//int& ra3 = 10;//编译错误,10为右值const int& ra3 = 10;return 0;
}
  • 右值引用:只能引用右值,不能引用左值。但是右值引用可以引用move之后的左值。move在后面有介绍,可以认为是改变了左值的属性,变成了右值。
int main(){//a为左值,10为右值int a = 10;int&& ra1 = 10;//int&& ra2 = a;//a为左值,编译错误int&& ra2 = move(a);//move后就可以了return 0;
}

二.右值引用的作用

右值引用和引用都是别名,为什么还要提出右值引用呢?

2.1引用的缺陷

#include<iostream>
using namespace std;class String{
private:char *_str;
public://构造String(char *str = " "){_str = new char[strlen(str) + 1];strcpy(_str, str);}//拷贝构造String(const String& s){cout << "String(const String& s)——拷贝构造" << endl;_str = new char[strlen(s._str) + 1];strcpy(_str, s._str);}};String Fun(String& s){String ret(s);return ret;
}
~String()
{if(_str)delete[] _str;
}
int main(){String s1("左值");String s2 = Fun(s1);getchar();return 0;
}

我们知道引用做参数和返回值是可以减少拷贝构造,特别是对于深拷贝的,可以提高效率。但是,当返回值是函数的局部对象时,不能引用返回,需要传值返回。

//需要传值返回
String Fun(String& s){String ret(s);return ret;
}

当将返回值函数返回值赋给另外一个对象s2时。 会调用String类的拷贝构造函数,进行深拷贝。

String s1("左值");
String s2 = Fun(s1);

ret在按照值返回时,必须拷贝构造一个临时对象,需要进行深拷贝。将Fun函数返回值赋值给s2时,也就是将临时对象拷贝构造s2,也需要进行深拷贝。仔细观察发现:s2和临时对象空间里的内容是相同的,而它们三个都有独立的空间,相当于创建了三个完全相同的对象。这样对于空间来说是一种浪费,程序效率也会降低。

下面来说如何优化。

2.1 移动语义

  • 移动语义:将一个对象中的资源移动到另外一个对象中。

如上缺陷,我们可以这样优化:

我们知道临时变量是内容和s2的内容相同,而临时对象是一个将亡值。我们可以在临时对象拷贝构造s2时,不进行深拷贝,而是将临时对象空间的资源换给对象s2。

临时对象是将亡值,也就是右值,我们可以重载一个参数为右值引用的拷贝构造函数。我们将这个拷贝构造函数称为移动构造。

参数s右值引用的是临时变量。将临时变量的资源移动到this指针,也就是s2中。而临时对象在构造完s2后,就会被销毁。

//移动构造
String(String&& s){_str = s._strs._str = nullptr;
}

整个过程:

        由于ret是左值,在拷贝构造临时对象时,调用的是拷贝构造函数,而临时对象是右值(将亡值)在构造s2时,会调用移动构造,将临时对象的资源换给了s2。这样减少了一次深拷贝的过程。

#include<iostream>using namespace std;class String{
private:char *_str;
public:String(char *str = " "){_str = new char[strlen(str) + 1];strcpy(_str, str);}//拷贝构造String(const String& s){cout << "String(const String& s)——拷贝构造" << endl;_str = new char(strlen(s._str) + 1);strcpy(_str, s._str);}//移动构造String(String&& s){cout << "String(const String& s)——移动构造" << endl;_str = s._str;s._str = nullptr;}~String(){if (_str)delete[] _str;}};//需要传值返回
String Fun(String& s){String ret(s);//调用拷贝构造return ret;
}int main(){String s1("左值");String s2(Fun(s1));//调用移动构造return 0;
}

这里编译器做了优化,ret调用拷贝构造函数构造临时对象的过程省略了。

注意:

  • 在移动构造函数的参数一定不能设置成const类型的右值引用,否则不能修改,资源无法转移。
  • 在C++11中,编译器会为了类默认生成一个移动构造,该移动构造为浅拷贝,因此当类中涉及到资源管理,必须显示自己的移动构造。

2.2 右值引用的具体应用

右值引用的主要应用就是重载了移动构造函数,利用了将亡值,将将亡值的空间内容交换到要拷贝的对象中。减少了深拷贝。

  • 右值引用做函数的参数

由于右值引用引用的是右值(将亡值),当函数体里需要对该参数进行拷贝构造时,会调用移动拷贝构造。减少深拷贝。提高效率。

  • 函数传值返回,用对象接收。

函数传值返回,返回一个临时对象,是一个将亡值。再用对象接收,临时对象拷贝构造对象。会调用移动拷贝构造函数,减少深拷贝。

2.3 对比引用总结

引用和右值引用本质的作用都是减少拷贝。右值引用弥补了引用的不足。右值引用提高了传值返回的效率。

引用:

引用做参数和返回值可以减少拷贝构造。但是,当返回的对象出了作用域就不在了,只能传值返回。

如果没有右值引用,用对象接收,会调用拷贝构造,对于string/vector等容器,需要进行深拷贝。效率低。

右值引用:

       右值引用的主要应用就是重载了移动构造函数,利用了将亡值,将将亡值的空间内容交换到要拷贝的对象中。减少了深拷贝。

        当函数传值返回时,用对象接收,会调用移动构造函数,对于string/vector等容器,不需要进行深拷贝,只需要将右值引用交换到构造的对象中即

右值引用做参数,函数体里有需要拷贝构造右值引用的,会调用移动构造,不会进行深拷贝。

三.右值引用引用左值(move)

按照语法,右值引用只能引用右值。但是在有些场景下,需要用到右值去引用左值来实现移动语义。

当右值引用一个左值,需要通过move函数将左值转化为右值。可以理解成将一个左值的属性改成右值返回。

int main(){//a为左值,10为右值int a = 10;int&& ra1 = 10;int&& ra2 = move(a);//move后就可以了return 0;
}

注意:被转化的左值,其生命周期并没有随着左值的转化而改变,move并不会销毁左值。但是,move后,会改变左值的内容。如果后序还会使用到左值,要慎用move。

在STL中也有一个move函数,作用时将一个范围中的元素搬到另外一个位置。

如下:用的上面这个类。

四.完美转化

转发是:按照模板参数的类型,将参数传递给函数模板中调用的另外一个函数。

#include<iostream>
using namespace std;void Fun(int &x){ cout << "lvalue ref" << endl;
}
void Fun(int &&x){ cout << "rvalue ref" << endl;
}template<typename T>
void PerfectForward(T &&t){Fun(t);
}int main()
{PerfectForward(10); //10是右值return 0;
}

下面PerfectForward()是转化的模板函数,Func为实际目标函数。但是这里有一个问题。如下:10是右值,但是调用的确是左值引用的函数。说明在PerfectForward()模板函数转发过程中,10的右值属性丢失了。

完美转发:是目标函数总希望参数的实际类型不会因为转化函数而发生改变。就好像转化函数不存在。也就是,当函数模板在向其它函数传递自身的形参时,如果相应实参时左值,它转化的就是左值,如果相应实参是右值,它转化的就是右值。

        完美转化需要通过forward函数来实现。

void Fun(int &x){ cout << "lvalue ref" << endl;
}
void Fun(int &&x){ cout << "rvalue ref" << endl;
}template<typename T>
void PerfectForward(T &&t){Fun(forward<T>(t)); //在需要转化函数的的目标函数参数调用forward函数
}
int main()
{PerfectForward(10); // rvalue refsystem("pause");return 0;
}

C++11——右值引用相关推荐

  1. C++11 右值引用、移动语义、完美转发、万能引用

    C++11 右值引用.移动语义.完美转发.引用折叠.万能引用 转自:http://c.biancheng.net/ C++中的左值和右值 右值引用可以从字面意思上理解,指的是以引用传递(而非值传递)的 ...

  2. C++11 右值引用和移动语义

    C++11 右值引用和移动语义 右值引用 左值与右值 对象的返回形式缺陷 ★移动语义 右值引用引用左值(move) 正确使用move的一个例子 完美转发 转发: 不转发: 右值引用作用 右值引用 C+ ...

  3. 【C++】C++11 右值引用和移动语义

    文章目录 一.左值与左值引用 二.右值与右值引用 三.左值引用和右值引用的比较 四.右值引用的使用场景和意义 1.左值引用的短板 2.移动构造和移动赋值 3.STL 容器的变化 五.万能引用与完美转发 ...

  4. move std 函数 示例_C++11右值引用和std::move语句实例解析(推荐)

    右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题.从语言本身讲,它健全了C++中的引用类型 ...

  5. C++11右值引用和移动构造函数

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105089274 右 ...

  6. C++ 11右值引用

    C++ 11中引入的一个非常重要的概念就是右值引用.理解右值引用是学习"移动语义"(move semantics)的基础.而要理解右值引用,就必须先区分左值与右值.        ...

  7. [C++11] 右值引用和移动语义

    c++11引入了右值引用和移动语义,通过避免无谓的复制,以提高程序的执行效率. 1.左值与右值 c++中的数值必属于左值或右值之一,通常有以下方法进行区分: 左值:在赋值语句左侧,右值:在赋值语句右侧 ...

  8. C++11右值引用和std::move语句实例解析

    关键字:C++11,右值引用,rvalue,std::move,VS 2015 OS:Windows 10 右值引用(及其支持的Move语意和完美转发)是C++0x加入的最重大语言特性之一.从实践角度 ...

  9. C++11右值引用、移动语义、完美转发详解

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

最新文章

  1. 如何在 Xcode 中修改应用的名字
  2. git在已忽略文件夹中不忽略指定文件
  3. 敏捷之道Scrum篇
  4. leetcode—Best Time to Buy and Sell stocks III
  5. stm32 GPIO简单介绍及初始化配置(库函数)
  6. pythonweb开发需要学哪些知识,python web需要了解哪些
  7. 作业要求 20181023-3 每周例行报告
  8. 【转载】水木算法讨论题
  9. 嵌入式电路设计(从电路到系统)
  10. Path.GetExtension 方法
  11. 产品经理这个梗是怎么来的?
  12. 经典算法-(六)老鼠走迷宫
  13. centos修改mysql数据库密码修改_centos7 mysql 修改数据库密码
  14. Dell重装系统之官方原版系统
  15. Linux虚拟机搜索文件/文件夹
  16. qq邮箱邮件被拦截如何找回的方法
  17. 根文件系统与文件系统的区别
  18. 论对B/S模式外贸电子商务系统的规划和设计
  19. rk3328 Android8.1 usb otg host和device切换
  20. MySQL8高级_读写分离和分库分表

热门文章

  1. iOS:新浪微博OAuth认证
  2. 女生头发怎么画?日系二次元女生头发画法
  3. 华为博士招聘上机考试题目_华为校园招聘上机考试题
  4. 手把手教你生成你的独家微信聊天年度报告
  5. 志宇-dubbo源码分析
  6. 网站合集(我收藏的网址)
  7. ICLOUD储存空间要升级吗_杭州楼市“吃紧”,楼市调控再升级,房价又要跌一波了吗?...
  8. CSP-J-2010-接水问题
  9. 【IPv6+燎原系列—第7期】天气渐凉,IPv6+将如何助力天气预报?
  10. 网站开发规范及流程v1.0