C++ 左引用和右引用
说明1:
首先我们区分什么是左值和右值,有一个很好区分左值和右值的方式,就是是否可以对表达式取地址!!可以获取地址的表达式就是左值,且持久性变量都是左值,反之则是右值。左值引用和右值引用都是对象的别名而已,左值引用就是左值的别名,右值引用就是右值的别名。所以左值引用只能绑定左值,右值引用只能绑定右值,我们不能将一个右值引用类型变量绑定到一个右值引用。因为变量都是左值!我们可以对右值引用类型变量取地址!但是有一个例外,const左值引用可以绑定到右值。这看起来好像有点难以理解,我们用代码实际解释一下。
int i = 42; //i是左值,可以对i取地址
int &r = i; //r是左值引用,绑定左值i
右值引用无法和右值引用类型变量绑定,因为右值引用类型变量是一个左值,所有持久性变量都是左值。可以取地址的变量是持久性变量,反之是临时性变量。
说明2:
我们可以将左值引用看为一个对象的别名。左值引用的声明包含一个可选的说明符列表,后面跟一个引用声明符。左值引用必须被初始化,且不能再指向另外一个对象或设置为null。:
在C++11中,引入了右值引入,可以通过一个可变引用指向rvalue,但是不能绑定到lvalue,因此右值引用可以检测一个值是否是临时对象。 类似于左值引用使用&,右值引用使用&&,可以是const/non-const
说明3:
在 C++ 中,引用类型是一种复合类型,可以分为左值引用、常引用和右值引用
在了解什么是左值引用和右值引用之前,我们需要先了解什么是左值和右值。由于 C++ 本身没有给出左值和右值的标准定义,
可以取地址的,有名称的,非临时的是左值
不可取地址,匿名的,临时的是右值
左值引用
左值引用是我们平时在编程时最常使用到的。引用的作用相当于是给变量取了“别名”。引用本身没有自己的内存地址,因此在定义的过程当中必须进行初始化,而且一旦和变量进行了绑定,则无法解绑并重新绑定。另外,左值引用只能和左值进行绑定。
常引用
常引用是指利用 const 修饰的引用类型。由于引用本身已经绑定不可解绑,因此所用的 const 引用都是底层 const,即引用对象不能改变(俗称常引用)。由于常引用引用的是常量,所以可以使用表达式,字符串字面值等常量为常引用进行初始化。
右值引用
右值引用是 C++ 新标准中引入的。右值引用的定义形式为:
右值引用的主要作用有两个:
实现移动语义
实现完美转发
作用一、移动语义的实现。
C++ 中的 “移动语义” 是相对于 “拷贝语义” 的一个概念。在引入”移动语义“之前,C++ 主要利用拷贝功能来实现对象的移动,通常的操作是在新分配好的内存空间上调用拷贝构造函数,将旧对象复制到新内容中,然后再释放旧对象。而”移动语义“的引入使得 C++ 允许直接将旧对象的所有权直接转让给新分配的内存空间,这样一来避免了旧对象的复制与释放,极大地提高了代码的运行效率。如果说 ”拷贝语义“ 描述了对象的拷贝功能,那么”移动语义“ 描述的则是对象的剪切功能。(注:转让意味着将所有权交给新区域的同时,还要放弃自身的所有权)
C++ 中的 ”移动语义“ 主要利用右值引用来实现。C++ 允许将一个右值引用 rr 绑定到一个临时对象上,从而将该临时对象的生命周期延长到与 rr 一样长。这样一来,我们便可以通过 rr 在程序当中的任何位置访问该临时对象。
作用 二:完美转发
完美转发主要应用于这样一组场景:我们需要将一个函数的参数原封不同地传递给另一个函数。在 C++ 中,函数参数除了类型和值以外,还有 const 和 non-const 以及 左值和右值 两组属性。所谓的完美转发,就是在将一个函数的参数传递给另一个参数时,参数的类型、值以及所有属性都不能改变,这在泛型编程当中有着广泛的应用。
针对模板函数的实现,C++ 11 在原有的正常绑定规则上增加一个新的绑定规则 —— 引用折叠,具体规则如下:
若模板函数 func 的形参类型为 T&, 则不论传递给 func 的实参是左值还是右值,一律统统折叠为左值引用
若模板函数 func 的形参类型为 T&&, 则传递给函数的左值实参折叠为左值引用,右值实参折叠为右值引用
总之一句话:在定义函数模板时使用右值引用做形参,能够保证参数的属性(const 属性和 左/右值属性)不发生变化。
在 代码 2 中,虽然 i 和 ci 是左值,但由于发生了引用折叠,所以 f1(i) 和 f1(ci) 是合法的。在引用折叠的规则下,无论一个函数有多少个参数,借助右值引用我们都只需要定义一个函数模板即可,这就大大减少了编程时的低效劳动。
右值引用和常引用的异同
相同点:
右值引用和常引用都允许和表达式,字符串字面值等进行绑定
不同点:
常引用可以和普通变量绑定,而右值引用只能绑定到右值上面
常引用类型的变量是不可修改的,而右值引用类型的变量是可以修改的
引用和指针的区别
引用和指针都是复合类型,但指针是变量,有自己的内存地址,而引用是别名,没有自己的内存地址
引用一旦定义就必须初始化,而指针可以先定义,然后再初始化
引用一旦和某个具体的变量绑定后,就不能再解除绑定,而指针变量在指向某一变量后,依然可以改变指向。
存在多维指针,但不存在多维引用【C++ 中不允许直接定义引用的引用,但可以通过类型别名或者模板类型参数间接定义】
因为引用没有自己的内存地址,所以只存在引用指针的引用,但不存在指向引用的指针
注:实际上,C++ 当中引用的实现也是利用到了 const 指针来实现的,但是由于编译器做了一部分工作,因此引用本身对于程序员而言是透明的。