本文主要介绍C++编程语言中引用(reference)的相关知识,同时通过示例代码介绍引用的使用方法。
1 概述
1.1 What
引用(reference),就是某一变量(目标)的一个别名,对引用的操作等同于对该变量(目标)的直接操作。
引用是C++语言对C语言的重要扩充。
引用名是变量的一个别名,即它是某个已经存在变量的另一个名字,一旦把引用初始化为某个变量,就可以使用该引用名称来指向那个变量。如果把变量名称作为变量附属在内存位置中的一个标签,那么就可以把对应的引用名称当成是变量附属在内存位置中的第二个标签。因此,可以通过原始变量名称(即原始标签)或引用名称(第二个标签)来访问变量的内容。
1.2 How
引用的声明方法与指针相似,只是用符号“&”代替了符号“*”。引用的声明方法如下:
类型标识符& 引用名 = 目标变量名
例如,有以下引用声明:
int i;
int& j = i;
在上述声明中,“&”读作“引用”,上述声明可以描述为“j是一个初始化为i的整型变量引用”。
1.3 引用和指针的区别
引用很容易与指针混淆,两者之间主要有以下区别:
- 不存在空引用,引用必须连接到一块合法的内存;
- 引用必须在声明时进行初始化,而指针可以在任何时候进行初始化;
- 一旦引用被初始化为一个对象(即变量、目标),就会被一直连接到这个对象对应的内存地址(不可更改),而指针可以在任何时候指向另一个对象(的内存地址)。
2 示例代码
下面通过几个示例代码来详细讲述引用的用法。
2.1 示例代码
给出一个简单的示例代码,内容如下:
#include <iostream>
using namespace std;
int main()
{
// 声明变量
int i;
// 声明引用变量
int& j = i;
// 定义变量
i = 5;
cout << "Value of i is: " << i << endl;
cout << "Value of j is: " << j << endl;
cout << "Addr of i is: " << &i << endl;
cout << "Addr of j is: " << &j << endl;
// 声明变量
int x;
// 声明变量
int y = x;
// 定义变量
x = 6;
cout << "Value of x is: " << x << endl;
cout << "Value of y is: " << y << endl;
cout << "Addr of x is: " << &x << endl;
cout << "Addr of y is: " << &y << endl;
return 0;
}
编译并运行上述代码,结果如下:
通过上述运行结果,可知:
- 使用引用时,引用变量“j”与原始变量“i”指向同一段内存空间;不使用引用时,新声明的变量“y”会开辟一块新的内存空间。
给出另外一个简单的示例代码,内容如下:
#include <iostream>
using namespace std;
int main()
{
// 声明变量
int i;
// 声明引用变量
int& j = i;
// 定义变量
i = 5;
cout << "Value of i is: " << i << endl;
cout << "Value of j is: " << j << endl;
cout << "Addr of i is: " << &i << endl;
cout << "Addr of j is: " << &j << endl;
// 定义变量
int k = 6;
// 改变引用变量j的值
j = k;
cout << "Value of k is: " << k << endl;
cout << "Value of j is: " << j << endl;
cout << "Value of i is: " << i << endl;
cout << "Addr of k is: " << &k << endl;
cout << "Addr of j is: " << &j << endl;
return 0;
}
编译并执行上述代码,结果如下:
通过上述运行结果,可知:
- 虽然在执行语句“j = k;”后,引用“j”的值变为了“6”,但是“j”仍然指向变量“i”的(内存)地址空间,这点没有发生改变,也就时说语句“j = k;”只是将“k”的值赋给引用“j”所指向的地址空间对应的变量(如“j”和“i”),并没有改变引用“j”的指向。
2.2 引用作为参数
C++之所以增加“引用”类型,主要是要将其作为函数参数,以扩充函数传递数据的功能。
在C++中进行函数传参,有以下几种情况:
- 将变量名作为实参和形参:这种情况下,传给形参的是变量的值,参数传递是单向的。如果在函数执行期间,形参的值发生了变化,(这种变化)不会影响到实参的值,即参数变化不回传,这是因为在调用函数期间,形参和实参不属于同一个存储单元。(C语言同理)
- 传递变量的指针:这种情况下,形参是指针变量,实参是变量的地址。在调用函数时,形参(指针变量)指向实参变量单元。这种传递指针变量的方式,通过改变形参指针所指向的内存地址对应的值,可以改变实参的值。(C语言同理)
- C++提供的传递变量的引用:这种情况下,形参是引用变量,和实参是同一个变量。在调用函数时,形参(引用变量)指向实参变量单元。这种传递引用变量的方式,通过改变引用的值,可以改变实参的值。
将引用作为函数参数,示例代码如下:
#include <iostream>
using namespace std;
// 函数声明
int swap(int& x, int& y);
int main()
{
// 局部变量声明
int a = 1;
int b = 2;
cout << "before swap, a is: " << a << endl;
cout << "before swap, b is: " << b << endl;
// 调用函数来交换值
swap(a, b);
cout << "after swap, a is: " << a << endl;
cout << "after swap, b is: " << b << endl;
return 0;
}
// 函数定义
int swap(int& x, int& y)
{
int temp;
temp = x;
x = y;
y = temp;
return 0;
}
编译并执行上述代码,结果如下:
从上述结果能够看到,通过调用swap函数,变量“a”和“b”的值发生了改变。
2.3 引用作为返回值
通过使用引用替代指针,可以提高C++程序的可读性和可维护性。
C++函数可以返回一个引用,(返回方式)与返回一个指针类似。当函数返回一个引用时,实际上是返回一个指向返回值的隐式指针。
使用引用作为函数返回值,主要有以下几个优点:
- 在函数返回时,内存中不会再生成返回值的临时变量了,从而提升了程序的时间和空间效率;
- 可以将函数放在赋值语句的左边了。
将引用作为函数返回值,示例代码的内容如下:
#include <iostream>
using namespace std;
int Array[] = {1, 2, 3, 4, 5};
int& SetValues(int i)
{
// 返回第i个元素的引用
return Array[i];
}
int main()
{
cout << "Before SetValues: " << endl;
for (int i = 0; i < 5; i++)
{
cout << "Array[" << i << "] = ";
cout << Array[i] << endl;
}
// 改变第2个元素的值
SetValues(1) = 20;
// 改变第4个元素的值
SetValues(3) = 40;
cout << "After SetValues:" << endl;
for (int i = 0; i < 5; i++)
{
cout << "Array[" << i << "] = ";
cout << Array[i] << endl;
}
return 0;
}
编译并执行上述代码,结果如下:
根据上述结果能够知道,通过将引用作为函数返回值,实现了将函数作为赋值语句左值、改变函数返回值的效果。
注意:当函数返回一个引用时,需要注意被引用的对象不能超出其作用域,所以返回一个局部变量的引用是不合法的(会出现告警),但是可以返回一个静态变量的引用,示例代码如下:
int& func()
{
int x;
// 在编译时会产生告警
return x;
static int y;
// 用法无误,因为y的作用域在函数外依然有效
return y;
}