引用
指针是继承于C语言的一种复合数据类型,定义与使用方法都和C语言一样,关于指针在这里就不再多讲。本节主要学习C++标准引入的一个新概念——引用。引用是隐式的指针,是C++常用的重要内容之一,灵活的使用引用可以使程序简洁高效。
1、引用的初始化
引用就是给一个变量起一个别名,用“&”标识符来标识,其格式如下所示。
数据类型 & 引用名 = 变量名;
上述格式中,“&”并不是取地址操作符,而是起标识作用,标识所定义的标识符是一个引用。引用声明完成以后相当于目标变量有两个名称,如下代码所示。
int a = 10;
int &b = a;
在上述代码中,b就是变量a的引用,b和a标识的是同一块内存,相当于一个人有两个名字。对a与b进行操作,都会更改内存中的数据。
在定义引用时有以下几点需要注意。
● 引用在定义时必须初始化,如int &b;语句是错误的。
● 引用在初始化时只能绑定左值,不能绑定常量值,如int &b = 10;语句是错误的。
● 引用一旦初始化,其值就不能再更改,即不能再做别的变量的引用。
● 数组不能定义引用,因为数组是一组数据,无法定义其别名。
引用实际上是隐式的指针,它与指针的区别主要是指针是一种数据类型而引用不是,指针可以转换为它所指向的变量的数据类型,如下代码所示。
int a = 10;
int *p = &a;
double *pd = (double*)p;
经过转换之后,pd指针就可以指向double类型数据,而引用则必须要和目标变量的数据类型相同,无法进行数据类型转换。相对指针,引用的定义与使用更为简单,与普通变量的操作相同。如下代码所示。
int a = 10;
int &b = a;
b = 20;
通过引用b改变变量的值,即a和b标识的内存中的值被改变了。
2、引用作为函数参数
C++增加引用的类型,主要的应用就是把它作为函数的参数,以扩充函数传递数据的功能,引用作函数参数时是区别于值传递与址传递的引用传递,接下来我们以交换两个数据的值为例来分析引用作为函数参数的用法,具体代码如例1所示。
例1
1 #include <iostream>
2 using namespace std;
3 void swap(int& x, int& y)
4 {
5 int temp = x;
6 x = y;
7 y = temp;
8 }
9 int main()
10 {
11 int a, b;
12 cout << "please input two nums: " << endl;
13 cin >> a >> b;
14 swap(a, b);
15 cout << "swap: " << a << " "<< b << endl;
16 system("pause");
17 return 0;
18 }
运行结果如图1所示。
图1 例1运行结果
由图1可知,输入的两个数据给a,b,然后调用swap()函数交换a与b的值,结果交换成功。在传递实参时,传入a与b两个变量,却起到了指针传递的作用。
这是一个典型的区分值传递与址传递的函数,如果是值传递,由于副本机制无法实现两个数据的交换;址传递则可以完成两个数据的交换,但也需要为形参(指针)分配存储单元,在调用时要反复使用“*指针名”,且实参传递时要取地址,这样很容易出现错误且程序的可读性也会下降。而引用传递就完全克服了它们的缺点,使用引用就是直接操作变量,简单高效可读性又好。
引用是隐式的指针,但使用引用与使用指针却有着本质的区别:
(1)使用引用类型就不必在swap()函数中声明形参是指针变量。指针变量要另外开辟内存单元,其内容是地址。而引用变量不是一个独立的变量,不单独占内存单元,在例1-9中引用变量a和b的值的数据类型与实参相同,都是整型。
(2)在main()函数中调用swap()函数时,实参不必用变量的地址(在变量名的前面加&),而直接用变量名。系统向形参传送的是实参的地址而不是实参的值。
显然,这种用法比使用指针变量简单、直观、方便。使用变量的引用,可以部分代替指针的操作。有些过去只能用指针来处理的问题,现在可以用引用来代替,从而降低了程序设计的难度。
有时候在传递参数时,不希望函数改变实参传入的值,也可以使用const来限定形参,就像使用常量指针一样,例如下面的函数,比较两个字符串长短(不是比较两个字符串大小)。
bool isLonger(const string &s1, const string &s2)
{
return s1.size() > s2.size();
}
在这个函数里,明显只希望比较两个字符串长短而不改变字符串,所以就用const来限定形参,这样别人就不能改变两个字符串的内容。关于引用作为函数参数,在后面的学习中读者要多加练习掌握。
3、引用与const限定符
在前面小节的学习中,我们知道引用不能绑定常量值,如果想要用常量值去初始化引用,则引用必须用const来修饰,这样的引用我们称之为const引用。相对于指针,引用与const结合使用是很简单的,只有非const引用与const引用两种,非const引用上一小节已经学习,本小节就简单学习一下const引用。
const引用可以用const对象和常量值来初始化,例如:
const int &a = 10; //常量值初始化const引用
const int a = 10;
const int &b = a; //const对象初始化const引用
一般来说对于const对象而言,只能采用const引用,如果没有对引用进行限定,那么就可以通过引用对数据进行修改,这是不允许的。但const引用不一定都得用const对象初始化,还可以用非const对象来初始化,例如:
int a = 10;
const int &b = a;
用非const对象初始化const引用,只是不允许通过该引用修改变量值。除此之外,const引用甚至可以用不同类型的变量来初始化const引用,例如:
double d = 1.2;
const int &b = d;
这是连指针都没有的优越性,此处b引用了一个double类型的数值,编译器在编译这两行代码时,先把d进行了一下转换,转换int类型数据,然后又赋值给了引用b,其转换过程如下代码所示:
double d = 1.2;
const int temp = (int) d;
const int &b = temp;
在这种情况下,b绑定的是一个临时变量。而当非const引用时,如果绑定到临时变量,那么可以通过引用修改临时变量的值,修改一个临时变量的值是没有任何意义的,因此编译器把这种行为定为是非法的,那么用不同类型的变量初始化一个普通引用自然也是非法的。