1. 左值

左值(lvalue,left value),顾名思义就是赋值符号左边的值,可以取地址。准确来说,左值是表达式(不一定是赋值表达式)后依然存在的持久对象。

可以将左值看作是一个关联了名称的内存位置,允许程序的其他部分来访问它。在这里,我们将 "名称" 解释为任何可用于访问内存位置的表达式。所以,如果 arr 是一个数组,那么 arr[1] 和 *(arr+1) 都将被视为相同内存位置的“名称”。

2. 右值

右值(rvalue,right value),右边的值,是指表达式结束后就不再存在的临时对象,不能取地址

纯右值(prvalue,pure rvalue),纯粹的右值,要么是纯粹的字面量,例如10, true;要么是求值结果相当于字面量或匿名临时对象,例如1+2。非引用返回的临时变量、运算表达式产生的临时变量、原始字面量、Lambda表达式都属于纯右值。

小结:相对而言,右值则是一个临时值,它不能被程序的其他部分访问。为了说明这些概念,请看以下程序段:

int square(int a){        return a * a;}int main(){        int x = 0; // 1        x = 12; // 2        cout << x << endl; // 3        x = square(5); // 4        cout << x << endl; // 5        return 0;}

在该程序中,x 是一个左值,这是因为 x 代表一个内存位置,它可以被程序的其他部分访问,例如上面注释的第 2、3、4 和 5 行。

而表达式 square(5) 却是一个右值,因为它代表了一个由编译器创建的临时内存位置,以保存由函数返回的值。该内存位置仅被访问一次,也就是在第 4 行赋值语句的右侧。在此之后,它就会立即被删除,再也不能被访问了。

对于包含右值的内存位置来说,其本质就是:它虽然没有名称,但是可以从程序的其他部分访问到它。

3. 左值引用

左值引用的声明是通过在某个类型后放置一个符号&来进行的。前文代码中的int & y = x;便是一个左值引用。

需要注意的是,在定义左值引用时,=右边的要求是一个可修改的左值。因此下面几种左值引用都是错误的:

#include <stdio.h>
int main(){    const int x = 5;    int y = 1;    int z = 1;    int & tmp1 = x;     // ERROR:x不是一个可修改的左值    int & tmp2 = 5;     // ERROR:5是一个右值    int & tmp3 = y + z; // ERROR:y+z是一个右值    return 0;}

左值引用的用途:

(1) 作为复杂名称变量的别名

我们可以写出类似如下的语句:

auto & whichList = theList[myHash(x, theList.size())];

可以看到,我们用简短的whichList代替了其原本复杂的名称,这能够简化我们的代码书写。

(2) 用于rangeFor循环

设想我们希望通过rangeFor循环使一个vector对象所有值都增加1,下面的rangeFor循环是做不到的、

for (auto x : arr)   // x仅相当于每个元素的拷贝    ++x;

但我们可以通过使用引用达到这一目的

for (auto & x : arr)    ++x;

(3) 避免复制大的对象

假定有一个findMax函数,它返回一个vector中最大的元素。若给定vector存储的是某些大的对象时,下述代码中的x拷贝返回的最大值到x的内存中:

auto x = finaMax(vector);

在大型的项目中这显然会增大程序的开销,这时我们可以通过引用来减小这类开销

auto & x = findMax(vector);

类似的,我们在处理函数返回值的时候也可以使用传引用返回。但是要注意,当返回的是类中私有属性时,传回的引用会导致外界能够对其修改。

(4) 参与函数中的参数传递

在C和C++的函数中,addSelf(int x)这类函数对直接传入的参数进行修改并不会改变原有参数的值。而有时我们希望能够实现类似swap(int a, int b)这类能够修改原参数的函数时,我们可以通过1.传入指针和2.传入引用实现。
swap函数的实现是一个很好的例子

#include <stdio.h>
void swap_non(int, int);    // 直接传入参数 void swap_p(int *, int *);  // 传入指针void swap_r(int &, int &);  // 传入引用
int main(){    int a = 1;    int b = 2;    printf("直接传入参数时:\n");    swap_non(a, b);    printf("a = %d, b = %d\n", a, b);    printf("传入指针时:\n");    swap_p(&a, &b);    printf("a = %d, b = %d\n", a, b);    printf("传入引用时:\n");    swap_r(a, b);    printf("a = %d, b = %d\n", a, b);    return 0;}
void swap_non(int a, int b){    int temp = a;    a = b;    b = temp;}
void swap_p(int * a, int * b){    int temp = *a;    *a = *b;    *b = temp;}
void swap_r(int & a, int & b){    int temp = a;    a = b;    b = temp;}

4. 右值引用

首先要提出一个新的概念: 将亡值(xvalue,expiring value),是C++11为了引入右值引用而提出的概念(因此在传统 C++中,纯右值和右值是同一个概念),也就是即将被销毁、却能够被移动的值。

C++11 引入了右值引用的概念,以表示一个本应没有名称的临时对象。右值引用的声明与左值引用类似,但是它使用的是 2 个 & 符号(&&),以下代码使用了右值引用打印了两次 5 的平方:

int && rRef = square(5);cout << rRef << endl;cout << rRef << endl;

有意思的是,声明一个右值引用,给一个临时内存位置分配一个名称,这使得程序的其他部分访问该内存位置成为了可能,并且可以将这个临时位置变成一个左值。

右值引用不能约束到左值上,所以,以下代码将无法编译:

int x = 0;int && rRefX = x;

再来看以下初始化语句:

int && rRef1 = square(5);

在初始化完成之后,这个包含值 square(5) 的内存位置有了一个名称,即 rRef1,所以 rRef1 本身变成了一个左值。这意味着后面的这个初始化语句将不会编译:

int && rRef2 = rRef1;

究其原因,就是右侧的 rRef1 不再是一个右值。综上所述,临时对象最多可以有一个左值引用指向它。如果函数有一个临时对象的左值引用,则可以确认,程序的其他部分都不能访问相同的对象。

右值引用会涉及到很多概念比如移动语义,完美转发,会在今后的文章中聊

引用和指针的区别

我们知道,指针是在内存中存放地址的一种变量,cpu能够直接通过而变量名访问唯一对应的内存单元,且每个内存单元的地址都是唯一的。
而变量名和引用,都可以看做内存的一个标签或是标识符,计算机通过是否符合标识符判断是否为目标内存,而一个内存可以有多个标识符

左值、右值、左值引用、右值引用相关推荐

  1. 左值引用——右值引用 详解

    顾明思议 左值引用 就是对左值的引用 就是给左值取别名 右值引用 就是对右值的引用 就是给右值取别名 当改变别名是 该值也相应的改变 那么 何以区分哪些是左值哪些是右值呢? 左值 右值 在内存中有特定 ...

  2. 判断是否左值引用/右值引用

    判断是否左值引用/右值引用 有时候搞不清楚推导出来的类型是左值引用还是右值引用,可以用接口辅助判断: int i = 0; std::is_lvalue_reference<decltype(+ ...

  3. C++11中的右值引用(对比左值引用和常引用)、移动构造函数和引用标识符

    Hello!各位同学们大家好!逗比老师最近说起来还是挺尴尬的,为什么这么说呢?因为以前我对自己的C++水平还是相当自信的,经常以"精通"来自我评价.但是最近发现自己好像对C++11 ...

  4. 深入浅出C++左值引用,右值引用,移动语义。

    什么是左值 右值? 简单来说左值就是可以取地址,在=左边的,而右值就是不可以取地址,在=右边的. int t=10; t可以通过&取地址在=左边 所以t是左值 10不可以取地址 在=右边10是 ...

  5. c++ | 左值引用 右值引用

    左值:可以取地址的.有名字的就是左值,比如 int a; 右值:不能取地址的是右值,表达式结束后就会被销毁, 比如 a*3 左值引用:就是普通引用,是为对象起的别名,必须被初始化,与变量绑定到一起 如 ...

  6. [c++]-c++中的左值和右值、左值引用和右值引用、万能引用和引用折叠及完美转发

    1.左值和右值 1.1左值和右值定义 在c++中,左值是一个指向内存的东西,换句话来讲,左值有地址,保存在内存中,右值则为不指向任何地方东西,即不在内存中占有确定位置.一般来说,右值是暂时和短暂的,而 ...

  7. 左值/右值/左值引用/右值引用/move的用法介绍

    目录 问题 左值和右值 概念总结: 需要用到左值的运算符: 引用分类 左值引用 右值引用 右值引用到底什么用? std::move()函数介绍 问题 什么是左值和右值? 什么是左/右值引用? 左/右值 ...

  8. std::move 左值右值 左值引用右值引用

    参考:https://blog.csdn.net/daaikuaichuan/article/details/88371948 https://zhuanlan.zhihu.com/p/9458820 ...

  9. 笛卡尔树 (25 分)笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字

    立志用最少的代码做最高效的表达 笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2.首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大.其次所有结点的 ...

  10. 2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码)

    2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码) 1.引用(普通引用) 变量 ...

最新文章

  1. R语言-路径设置与工作目录修改
  2. 俄罗斯拟明年在36万台华为平板安装“极光”操作系统
  3. 学习笔记------tag文件
  4. 简单入门循环神经网络RNN:时间序列数据的首选神经网络
  5. 逻辑回归阈值_逻辑回归or线性回归,傻傻分不清楚
  6. 功率增长步长(powerRampingStep)
  7. 2020 云原生技术 7 大领域趋势全预测
  8. 牛客网暑期ACM多校训练营(第五场)A-gap (二分答案)
  9. GDB 调试多进程或者多线程应用
  10. 牛客多校三 B Black and white
  11. boost::io::ostream_put用法的测试程序
  12. java lambda例子_Java lambda 表达式常用示例
  13. Transactional cannot be resolved to a type
  14. java操作ssdb:set、map、list..
  15. 矩阵乘法(信息学奥赛一本通-T1125)
  16. HttpHelper使用记录
  17. PHP数据库扩展 - PDO操作
  18. 长虹电视刷机固件包汇总
  19. 基于Matlab人脸识别(PCA算法)
  20. 汉字转拼音(支持多音字)

热门文章

  1. reac——父组件向子组件传递值,子组件何时能同步获得父组件改变后的值
  2. 直播技术初体验,简单实现直播不同阶段
  3. android学汇资料总整理
  4. oracle参数文件initorcl位置,oracle 参数文件详解
  5. 核心概念——节点/边/Combo——内置节点——Triangle
  6. BASIC-4 数列特征
  7. 机器学习 —— python库 —— 使用array创建
  8. python人工智能——机器学习——转换器与估计器
  9. 【机器视觉】 dev_map_var算子
  10. 【Protocol Buffer】Protocol Buffer入门教程(六):枚举和包