引用(reference)是一个现有对象的别名。用对象来初始化引用之后,那么对象的名字或引用的名字都可以用于指向(refer to)该对象:
int a = 12;
int &ra = a;
--ra; // a == 11;
a = 10; // ra = 10;
int *ip = &ra;
人们常常会将引用和指针相混淆,原因大概在于C++编译器通常采用指针的方式实现引用,但引用其实不是指针,其行为和指针并不相同。
在引用和指针之间存在三大区别:其一,不存在空引用(NULL Reference);其二,所有引用都要初始化;其三,一个引用永远指向用来对它初始化的那个对象。比如说,在先前的例子中,引用ra在整个生命期内都指向a。绝大多数对引用的误用都滋生于对这三大区别的理解。
一些编译器可以捕捉到那些明显的创建空引用的尝试:
Employee &anEmployee = *static_cast<Employee*>(0);
然而,编译咕嘟可能无法侦测到不那么明显的创建空引用的尝试,从而导致在运行期发生未定义的行为:
Employee *getAnEmployee();
//...
Employee &anEmployee = *getAnEmployee(); // 可能是糟糕的代码
if (&anEmployee == 0)
如果getAnEmployee返回的是一个空指针,那么其后代码的行为就是未定义的。在这个例子中,最好使用一个指针来存放getAnEmployee返回的结果。
Employee *employee = getAnEmployee();
if (employee)//...
引用必须初始化的要求,意味着当一个引用初始化它所指向的那个对象必须存在。这一点很重要,因此我想再重复一遍:一个引用就是在该引用被初始化之前已经存在的一个对象的别名。一旦一个引用被初始化去指向一个特定的对象,那么该引用以后就不可以再指向别的对象;在一个引用的整个生命期内,该引用被绑定到用于初始化它的那个对象上。实际上,一个引用完成其初始化后,就只是初始化它的那个对象的别名了。这个“别名”属性使得引用常常成为形参的优秀选择。在以下swap函数模板中,形参a和b乃是传递给调用的实参的别名:
template<typename T>
void swap(T &a, T &b)
{
T temp(a);
a = b;
b = temp;
}
//...
int x = 1, y = 2;
swap(x, y);