声明:资料整理自网络资源,未能全部注明引用来源,如有侵权请联系。
一、基本概念
引用(Reference)是 C++ 相对于C语言的又一个扩充。 引用变量是一个别名,即某个已存在变量的另一个名字。 声明方法: 类型标识符 &引用名=目标变量名;int a; //定义变量a
int &b = a; //定义引用b,a和b表示相同的变量,具有相同的地址
注意:
1,引用不占内存空间,必须在定义的同时初始化,且不能再引用其他数据。
2,引用在定义时需要添加&,在使用时不能添加&,使用时添加&表示取地址。
二、应用场景
2.1 参数传递
引用变量的主要用途是用作函数的形参,按引用传递参数。 通过将应用变量用作参数,函数将使用原始数据,而不是其副本。 与复制原始结构的拷贝(按值传递)相比,使用引用可节省时间和内存; 在返回值为引用时,其效率更高,应避免返回函数终止时不再存在的内存单元引用。 将类对象传递给函数时,通常使用引用。 引用通常用于函数参数列表和函数返回值概念 | 描述 |
---|---|
把引用作为参数 | C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。 |
把引用作为返回值 | 可以从 C++ 函数中返回引用,就像返回其他数据类型一样。 |
2.1.1 传引用调用
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。 (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给 形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效 率和所占空间都好。 (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的 形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。 如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。2.1.2 传常量引用调用
常引用声明方式:const 类型标识符 &引用名=目标变量名; 用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。 引用型参数应该在能被定义为const的情况下,尽量定义为const 。 总结: 1,对于小的不应被函数改变的对象,采取传值调用是合适的。 2,对于大的不应被函数改变且复制代价昂贵的对象,采取传常量调用时合适的。 3,对于所有可以被函数改变的对象,采取传引用调用时合适的。2.3 返回值传递
要以引用返回函数值,则函数定义时要按以下格式: 类型标识符 &函数名(形参列表及类型说明) {函数体} 说明: (1)以引用返回函数值,定义函数时需要在函数名前加& (2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。#include <iostream.h>
float temp; //定义全局变量temp
float fn1(float r); //声明函数fn1
float &fn2(float r); //声明函数fn2
float fn1(float r) //定义函数fn1,它以返回值的方法返回函数值
{
temp=(float)(r*r*3.14);
return temp;
}
float &fn2(float r) //定义函数fn2,它以引用方式返回函数值
{
temp=(float)(r*r*3.14);
return temp;
}
void main() //主函数
{
float a=fn1(10.0); //第1种情况,系统生成要返回值的副本(即临时变量)
float &b=fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定)
//不能从被调函数中返回一个临时变量或局部变量的引用
float c=fn2(10.0); //第3种情况,系统不生成返回值的副本
//可以从被调函数中返回一个全局变量的引用
float &d=fn2(10.0); //第4种情况,系统不生成返回值的副本
//可以从被调函数中返回一个全局变量的引用
cout<<a<<c<<d;
}
引用作为返回值,必须遵守以下规则:
(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一 个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。
三、注意事项
(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的, 引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。 (2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率, 且通过const的使用,保证了引用传递的安全性。 (3)引用与指针的区别 1,不存在空引用。引用必须连接到一块合法的内存。
2,一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
3,引用必须在创建时被初始化。指针可以在任何时间被初始化。
4,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;
而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
5,选择成员的时候,引用使用点 . 来查找,而指针则使用 -> 来查找。
6,指针可能传递一个 NULL 过来,因此在使用前必须检查有效性;引用则必然代表某个对象,不需要做此检查。