一:引用的概念及用法
1.引用概念
引用是已经定义的变量的别名(另一个名称),而不是定义一个新的变量。引用的主要用途是用作函数的形参,通过将引用变量用作函数形参,函数将操作原始数据而不再操作实参的一份临时拷贝(与指针变量作为参数类似),为函数处理大型结构等提供了一种非常方便的途径。
2.引用用法
C++给“&”符号赋予了另一个含义(一个是取地址用算符)将其用来申明引用。
上图中将 b 作为 a 的别名,此时的“&”不是取地址用算符,而是类型标识符的一部分,就像申明中 char* 指的是指向 char 的指针一样,int& 指的是指向 int 的引用。
从上图可知 a 和 b 的值和地址都相等,a 和 b 中任何一个的改变将影响到另一个变量。
二:引用和指针的区别
看到上图你可能会说引用是伪装了的指针。实际上引用与指针还是有很多差别的:
a:引用必须在其申明时将其初始化(指针可以先申明再赋值),并且只能初始化一次,之后不能将其改变为指向其他变量的引用(要从始至终。其实引用更接近于 const 指针,必须在创建时初始化,一旦与某个变量关联起来就将效忠于它。int& b = a; 实际上是 int* const p = &a;的伪装);b:引用必须指向有效变量,指针可以指向空;c:sizeof指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址的大小;d: 指针和引用自加(++)自减(--)意义不一样;e:相对而言,引用比指针更安全。
三:引用做参数
1.基本认识
引用传递的特性是使函数中的变量名成为调用程序中变量的别名,这样被调用函数可以访问调用函数中的变量。以交换函数为例,通过下图直观感受按值传递和按引用传递
上面两个图分别是按值传递和按引用传递,按值传递时变量 x 和 y是复制了 a 和 b的值的新变量,因此交换 x 和 y 的值不会影响到 a 和 b,这种方式是无法实现交换函数的;按引用传递函数被调用时用实参初始化形参。
2.临时变量、引用参数和const
如果引用参数用 const 修饰,编译器在下面两种情况下生成临时变量
a:实参的类型真确,但不是左值
b:实参的类型不正确,但可以转化为正确的类型
左值是可以被引用的数据对象,例如,变量、结构成员、引用和数组元素等(数组元素的行为与同类型的元素类似),常规变量和 const 变量(不可修改的左值)都可视为左值,因为可通过地址访问它们。非左值包括字面常量(字符串除外)和包含多项的表达式。
以上是参数已被 const 修饰的 cube 函数的几种调用,第一次调用时 a 是有名称的、double 类型的数据对象,因此为其可以创建引用,而不需要临时变量;第二次和第三次调用时 3.0 和 a+1.0 的类型都正确,但没有名称,第四次调用时 b 是变量,类型却不正确(double 引用不能指向 long),在这些情况下编译器将生成一个临时匿名变量,并让 ra 指向它(临时匿名变量只在函数调用期间存在,此后编译器可随意删除)。
应尽可能将引用形参用 const 修饰,理由如下:
a:使用 const 可以避免无意中修改数据的编程错误;
b:使用 const 使函数能处理 const 和非 const 实参;
c:使用 const 使函数能够正确生成并使用临时变量。
注意:当引用参数没被 const 修饰并且传递的实参没有名称或类型不匹配时现代编译器会阻止生成临时变量,这种情况下函数调用会失败。
五:引用做返回值
上图分别是传统返回机制和按引用返回的实例,传统返回机制下 ret 的值被复制到一个临时位置,调用函数使用这个值;按引用返回时调用函数直接使用 ret 的值,其效率更高。使用引用返回注意以下两点:
a :不要返回一个临时变量的引用。
b: 如果返回对象出了当前函数的作用域依旧存在,则最好使用引用返回,因为这样更高效。
六:汇编层看引用的特性
上图是按引用返回时的汇编层,不难看出它是把 ret 的地址保存到 exe 寄存器中;而传统返回机制是把 ret 的值保存到 exe 寄存器中。