引用(reference)是一种复合类型(compound type)。引用为对象起了另外一个名字,引用类型引用(refer to)另外一种类型。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名。

一、一般引用:

一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重写绑定到另外一个对象,因此引用必须初始化。

为引用赋值,实际上是把值赋给了与引用绑定的对象。获取引用的值,实际上是获取了与引用绑定的对象的值。同理,以引用作为初始值,实际上是以与引用绑定的对象作为初始值。

引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字,引用即别名。定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。

因为引用本身不是一个对象,所以不能定义引用的引用。

允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头。

引用只能绑定到对象上,而不能与字面值或某个表达式的计算结果绑定在一起。

二、const的引用:

可以把引用绑定到const对象上,就像绑定到其它对象上一样,称之为对常量的引用(reference to const)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。

常量的引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其它途径改变它的值。

三、传引用参数:

通过使用引用形参,允许函数改变一个或多个实参的值。

使用引用避免拷贝:拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本就不支持拷贝操作。当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。

使用引用形参返回额外信息:一个函数只能返回一个值,然而有时函数需要同时返回多个值,引用形参为一次返回多个结果提供了有效的途径。

函数参数是否需要传引用的判别:(1)、需要在函数内部改变实参的值;(2)、函数传入值较大,为避免拷贝的代价,使用引用。

在函数调用时在内存中不会生成副本,用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。

Advantages of passing by reference:

(1)、It allows a function to change the value of the argument, which is sometimes useful.Otherwise, const references can be used to guarantee the function won’t change the argument.

(2)、Because a copy of the argument is not made, it is fast, even when used with large structs or classes.

(3)、References can be used to return multiple values from a function.

(4)、References must be initialized, so there’s no worry about null values.

Disadvantages of passing by reference:

(1)、Because a non-const reference cannot be made to an rvalue (e.g. a literal or an expression), reference arguments must be normal variables.

(2)、It can be hard to tell whether a parameter passed by non-const reference is meant to be input, output, or both. Judicious use of const and a naming suffix for out variables can help.

(3)、It’s impossible to tell from the function call whether the argument may change. An argument passed by value and passed by reference looks the same. We can only tell whether an argument is passed by value or reference by looking at the function declaration. This can lead to situations where the programmer does not realize a function will change the value of the argument.

When to use pass by reference:

(1)、When passing structs or classes (use const if read-only).

(2)、When you need the function to modify an argument.

When not to use pass by reference:

(1)、When passing fundamental types (use pass by value).

四、右值引用:

为了支持移动操作,C++11引入了一种新的引用类型----右值引用(rvalue reference)。所谓右值引用就是必须绑定到右值的引用。通过&&而不是&来获得右值引用。右值引用有一个重要的性质----只能绑定到一个将要销毁的对象。因此,可以自由地将一个右值引用的资源”移动”到另一个对象中。

一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。

类似任何引用,一个右值引用也不过是某个对象的另一个名字而已。对于常规引用(为了与右值引用区分开来,可以称之为左值引用(lvalue reference)),不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。右值引用有着完全相反的绑定特性:可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用直接绑定到一个左值上。

返回左值引用的函数,连同赋值、下标、解引用和前置递增/递减运算符,都是返回左值得表达式的例子。可以将一个左值引用绑定到这类表达式的结果上。

返回非引用类型的函数,连同算术、关系、位以及后置递增/递减运算符,都生成右值。不能将一个左值引用绑定到这类表达式上,但可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。

左值持久,右值短暂:左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。

由于右值引用只能绑定到临时对象,可知:所引用的对象将要被销毁;该对象没有其它用户。这两个特性意味着,使用右值引用的代码可以自由地接管所引用的对象的资源。

变量是左值:变量可以看作只有一个运算对象而没有运算符的表达式。

变量是左值,因此不能将一个右值引用直接绑定到一个变量上,即使这个变量是右值引用类型也不行。

虽然不能就将一个右值引用直接绑定到一个左值上,但可以显示地将一个左值转换为对应的右值引用类型。还可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用,此函数定义在头文件utility中。move调用告诉编译器:有一个左值,但希望像一个右值一样处理它。在调用move之后,我们不能对移后源对象的值做任何假设。

我们可以销毁一个移后源对象,也可以赋予它新值,但不能使用一个移后源对象的值。

关于C++11中右值引用的更多介绍可以参数:http://blog.csdn.net/fengbingchun/article/details/52562004

五、引用作为函数返回值

以引用返回函数值,定义函数时需要在函数名前加&。用引用返回一个函数值得最大好处是,在内存中不产生被返回值的副本。

引用作为返回值,必须遵守以下规则:

(1)、不能返回局部变量的引用。

(2)、不能返回函数内部new分配的内存的引用。

(3)、可以返回类成员的引用,但最好是const。

当函数返回值类型为引用时,一般就用引用类型去接收,如果用非引用类型接收,就等于将函数返回的引用的数据值,复制给了该接收对象,和函数返回非引用类型是一样的效果。

不可以将常引用当作函数返回值返回。

能用常引用的地方尽量用常引用。

以下是测试代码:

#include "reference.hpp"
#include <iostream>
#include <utility> // std::moveint test_reference_1()
{// 1. 普通的引用int ival = 1024;int &refVal = ival; // refVal指向ival(是ival的另一个名字)//int &refVal2; // error:引用必须被初始化int &refVal3 = refVal; // refVal3绑定到了那个与refVal绑定的对象上,这里就是绑定到ival上// 利用与refVal绑定的对象的值初始化变量iint i = refVal; // correct:i被初始化为ival的值// 允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头int i1 = 1024, i2 = 2048;int &r = i1, r2 = i2;int i3 = 1024, &ri = i3;int &r3 = i3, &r4 = i2; // r3和r4都是引用//int &refVal4 = 10; // error: 引用类型的初始值必须是一个对象double dval = 3.14;//int &refVal5 = dval; // error: 此处引用类型的初始值必须是int型对象const int &refVal6 = dval; // correct// 编译器把上述语句变成了如下形式://const int temp = dval; // 由双精度浮点数生成一个临时的整型常量//const int &refVal6 = temp; // 让refVal6绑定这个临时量// 2. const的引用// 对常量的引用不能被用作修改它所绑定的对象const int ci = 1024;const int &r1 = ci; // correct: 引用及其对应的对象都是常量//r1 = 42; // error: r1是对常量的引用//int &r2 = ci; // error: 试图让一个非常量引用指向一个常量对象int i4 = 42;const int &r6 = i4; // 允许将const int&绑定到一个普通int对象上const int &r7 = 42; // correct: r7是一个常量引用const int &r8 = r6 * 2; // correct: r8是一个常量引用//int &r9 = r6 * 2; // error: r9是一个普通的非常量引用// 常量的引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定int x = 42;int &rx = x; // 引用rx绑定对象xconst int &ry = x; // ry也绑定对象x,但是不允许通过ry修改x的值rx = 0; // rx并非常量,x的值修改为0//ry = 0; // error: ry是一个常量引用return 0;
}// 该函数接受一个int对象的引用,然后将对象的值置为0
static void reset(int &i) // i是传给reset函数的对象的另一个名字
{i = 0; // 改变了i所引用对象的值
}int test_reference_2()
{int j = 42;reset(j); // j采用引用方式,它的值被改变fprintf(stderr, "j = %d\n", j); // j = 0return 0;
}int test_reference_3()
{int i = 42;int &r = i; // correct: r引用i//int &&rr = i; // error: 不能将一个右值引用绑定到一个左值上//int &r2 = i * 42; // error: i*42是一个右值const int &r3 = i * 42; // correct: 可以将一个const的引用绑定到一个右值上int &&rr2 = i * 42; // correct: 将rr2绑定到乘法结果上int &&rr1 = 42; // correct: 字面常量是右值//int &&rr3 = rr1; // error: 表达式rr1是左值int &&rr4 = std::move(rr1); // correctreturn 0;
}

GitHub: https://github.com/fengbingchun/Messy_Test

C++/C++11中引用的使用相关推荐

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

    http://www.cnblogs.com/yanqi0124/p/4723698.html 在C++98中有左值和右值的概念,不过这两个概念对于很多程序员并不关心,因为不知道这两个概念照样可以写出 ...

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

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

  3. (译)C++11中的Move语义和右值引用

    郑重声明:本文是笔者网上翻译原文,部分有做添加说明,所有权归原文作者! 地址:http://www.cprogramming.com/c++11/rvalue-references-and-move- ...

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

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

  5. C++11中头文件type_traits介绍

    C++11中的头文件type_traits定义了一系列模板类,在编译期获得某一参数.某一变量.某一个类等等类型信息,主要做静态检查. 此头文件包含三部分: (1).Helper类:帮助创建编译时常量的 ...

  6. C++11中std::shared_future的使用

    C++11中的std::shared_future是个模板类.与std::future类似,std::shared_future提供了一种访问异步操作结果的机制:不同于std::future,std: ...

  7. C++11中std::future的使用

    C++11中的std::future是一个模板类.std::future提供了一种用于访问异步操作结果的机制.std::future所引用的共享状态不能与任何其它异步返回的对象共享(与std::sha ...

  8. C++/C++11中用于定义类型别名的两种方法:typedef和using

    类型别名(type alias)是一个名字,它是某种类型的同义词.使用类型别名有很多好处,它让复杂的类型名字变得简单明了.易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的.在C++中,任何 ...

  9. C++/C++11中头文件functional的使用

    <functional>是C++标准库中的一个头文件,定义了C++标准中多个用于表示函数对象(function object)的类模板,包括算法操作.比较操作.逻辑操作:以及用于绑定函数对 ...

最新文章

  1. 转载-centos网络配置(手动设置,自动获取)的2种方法
  2. mysql8安装错误_Windows安装MySQL8.0.16 的步骤及出现错误问题解决方法
  3. ubuntu 设置大小写切换隐藏_VirtualBox中ubuntu的LAMP项目(温度采集)
  4. RTX3090 Super曝光:完整GA102核心加持、性能提升5%
  5. 浏览器卡怎么办_【十全十美】宽带断线、wifi连不上怎么办?自助排障帮到你!...
  6. 工作总结:文件对话框的分类(C++)
  7. android horizontalscrollview 动画,Android HorizontalScrollView左右滑动效果
  8. OpenJudge NOI 1.8 15:细菌的繁殖与扩散
  9. 最常用的数据库脚本前十名
  10. python死锁案例_Python 多线程死锁
  11. UVA11384 Help is needed for Dexter【数学】
  12. 电商平台实战经验:电商中的Hadoop生态系统应用
  13. 《把时间当作朋友》——运用心智获得解放 读书笔记(2)
  14. xp如何修改SID.
  15. Hadoop Partitioner 实战详解
  16. 基于群晖DS216+II的家庭存储解决方案
  17. Electron那些事10:本地数据库sqlite
  18. AM3352的I2C驱动与传感器sht20的应用
  19. yate--sip server的学习过程
  20. Android吃透inflate方法(二)

热门文章

  1. opencv-mediapipe手部关键点识别
  2. OpenCV(实战)二值图颜色填充(彩色图形、硬币)
  3. chrome 禁用https限制http_HTTP协议走过29年漏洞百出:Firefox可直接禁用
  4. c++控制台应用每一列数据如何对齐_Python数据分析第五节 pandas入门
  5. 基于简化点云地图的语义边缘对齐的单目定位方法
  6. Qt中openGL的四个重要事件(initializeGL() resizeGL() paintGL() pdateGL())调用规则
  7. 禁止缩放safari浏览器--阻止双击放大--阻止双指掐捏放大-
  8. 【力扣网练习题】罗马数字转整数
  9. vim学习笔记(一)
  10. 虚幻中的风格化环境制作学习教程