C/C++-左值、右值及引用
C和C++中定义了引用类型(reference type),存在左值引用(lvalue reference)。而在C++11中,新增了右值引用(rvalue reference)这一概念, 虽然个人感觉右值引用用处不大,但在此一并讨论。
1.左值and右值
首先,我们讨论左值和右值两个概念。
左值(lvalue):一个标识非临时性对象的表达式。通常来说,可以将程序中所有带名字的变量看做左值。
右值(rvalue):相对的,右值标识是临时性对象的表达式,这类对象没有指定的变量名,都是临时计算生成的。
考虑以下代码:
vector<string> arr(3);
const int x = 2;
int y;
int z = x + y;
string str = "foo";
vector<string> *ptr = &arr;
在上述代码中,arr, str, y, z等都是左值,x也是一个左值,且他不是一个可修改的左值;而类似于2, x+y这类临时(没有专属变量名)的值则是右值。
2.引用
我们可以这样理解引用:一个引用是它所引用对象的同义词,是其另一个变量名。
看这样一段代码:
#inclulde <stdio.h>
int main()
{
int x = 5;
int & y = x;
printf("引用y = %d\n", y);
return 0;
}
运行结果如下:

(1)左值引用
左值引用的声明是通过在某个类型后放置一个符号&来进行的。前文代码中的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;
}
编译运行,报错如下:

(2)右值引用
类似于左值引用,右值引用便是对右值的引用,它是通过两个&&来声明的。
#include <stdio.h>
int main()
{
int && x = 5;
printf("x = %d\n", x);
return 0;
}
运行结果如下:

引用和指针的区别
我们知道,指针是在内存中存放地址的一种变量,cpu能够直接通过而变量名访问唯一对应的内存单元,且每个内存单元的地址都是唯一的。
而变量名和引用,都可以看做内存的一个标签或是标识符,计算机通过是否符合标识符判断是否为目标内存,而一个内存可以有多个标识符
3.左值引用的用途
因为目前右值引用用途不大,故此处仅列举较为常见的左值引用用途
(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.std::move和std::swap
前面我们讨论3.(3)时,谈到可以使用引用来减少复制产生的内存开销。
考虑这样一种情况,在进行元素交换时,我们通常使用一个缓存变量temp来临时保存数据;而对temp直接进行=的赋值操作时,实际上temp复制了一次原有对象的内存,但我们需要只是对象之间的移动而不是复制,而C++STL中的std::move函数便可以达成这一操作。
这可能有些抽象,让我们通过例子来看看:
void swap(vector<string> & x, vector<string> & y)
{
vector<string> temp = std::move(x);
x = std::move(y);
y = std::move(temo);
}
上述例子是C++STL中std::swap的源码之一,相信它很好的示范了std::move是如何使用的,而在源码中使用,也足以说明它的高效。
C/C++-左值、右值及引用的更多相关文章
- C++ 左值与右值 右值引用 引用折叠 => 完美转发
左值与右值 什么是左值?什么是右值? 在C++里没有明确定义.看了几个版本,有名字的是左值,没名字的是右值.能被&取地址的是左值,不能被&取地址的是右值.而且左值与右值可以发生转换. ...
- C++11之右值引用(一):从左值右值到右值引用
C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是 ...
- C++ 左值 右值
最近在研究C++ 左值 右值,搬运.收集了一些别人的资料,供自己记录和学习,若以后看到了更好的解释,会继续补充.(打“?”是我自己不明白的地方 ) 参考:<Boost程序库探秘——深度解析C ...
- 左值&右值
一.引子 我们所谓的左值.右值,正确的说法应该是左值表达式.右值表达式. 因为C++的表达式不是左值就是右值. 在C中,左值指的是既能够出现在等号左边也能出现在等号右边的表达式,右值指的则是只能出现在 ...
- C语言几个术语: 数据对象,左值,右值
1. 数据对象 赋值表达式语句的目的是把值存储到内存位置上. 用于存储值的数据存储区域统称为数据对象. 2. 左值 左值是C语言的术语, 用于标识特定数据对象的名称或表达式. 对象指的是实际的数据存储 ...
- c++ 左值右值 函数模板
1.先看一段代码,这就是一种函数模板的用法,但是红色的部分如果把a写成a++或者写成一个常量比如1,都是编译不过的,因为如果是a++的话,实际上首先是取得a的 值0,而0作为一个常量没有地址.写成1也 ...
- i++和++i以及左值,右值
左值(LValue)和右值(RValue)的一个快捷记法是赋值运算,左值是赋值运算左边的值,右值就是右边(=,=废话).例如: int a = 5; a就是左值,5就是右值. 当然,如果真是这么个含义 ...
- C和C指针小记(八)-操作符、左值右值
1.移位操作符 移位操作符分为左移操作符(<<)和右移操纵符(>>) 对于无符号数:左右位移操作都是逻辑位移 对于有符号数:到底是采用逻辑位移还是算术位移取决于编译器.如果一个 ...
- 【C/C++开发】C++11:左值引用VS右值引用
左值引用VS右值引用 左值引用对于一般的C++程序员再熟悉不过,但对于右值引用(C++0X新特性),就稍微有点不知所云 左值VS右值 在定义变量的时候,经常会用到左值和右值,比如:int a = 1; ...
- <五>掌握左值引用和初识右值引用
1:C++的引用,引用和指针的区别? 1:从汇编指令角度上看,引用和指针没有区别,引用也是通过地址指针的方式访问指向的内存 int &b=a ; 是需要将a的内存地址取出并存下来, b=20; ...
随机推荐
- Java的技术体系结构
作为程序开发者,我们都想写出完美的代码,但世界上好像从来都没有过完美的代码,因为代码牵涉的内容很复杂,有程序设计语言.运行环境.数据结构以及算法等等,而开发者往往很难全面精通,再者写代码本来也就是一个 ...
- 菜鸟笔记 -- Chapter 4 Java语言基础
在Chapter3中我们写了第一个Java程序Hello World,并且对此程序进行了分析和常见错误解析.那么我们有没有认真观察一下Java程序的基本结构呢?本节我就来聊一下Java程序的基本结构( ...
- js数组去重方法整理
1.思路:定义一个新数组,并存放原数组的第一个元素,然后将原数组的项和新数组的元素一一对比,若不同则存放在新数组中. function unique(arr){ var res = [arr[0]]; ...
- 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester
这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...
- 没有上司的舞会(树形DP)
题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...
- duilib属性列表
<?xml version="1.0" encoding="UTF-8"?> <!-- 可能有错漏,欢迎补充.wangchyz(wangchy ...
- poj_2773_Happy 2006
Two positive integers are said to be relatively prime to each other if the Great Common Divisor (GCD ...
- Spring+SpringMVC+Mybatis(二)
上一次讲的是利用mybatis提供的sqlSessionTemplate作为DAO进行数据库的操作,其实我们可以把它封装到我们自己的DAO里面,这样就是所谓的自己写DAO,这次我们写一下通过mybat ...
- 常见的Dom操作
1.什么是DOM? DOM又称文档对象模型( DOM, Document Object Model )主要用于对HTML和XML文档的内容进行操作.DOM描绘了一个层次化的节点树,通过对节点进行操作, ...
- C#中的线程(二)线程同步基础 (读后感)
参考文章:https://www.cnblogs.com/dingfangbo/p/5769501.html 一.lock 确保只有一个线程访问某个资源或某段代码.通俗的讲就是多个线程操作相同的锁对象 ...