引用即别名
引用(reference) 为对象起了另外一个名字,引用类型引用(refers to)另外一种类型。通过将声明符写成 &d
的形式来定义引用类型,其中 d
是声明的变量名:
int ival = 1024;
int &refVal = ival; // refVal 指向 ival (是 ival 的另一个名字)
int &refVal2; // 报错:引用必须被初始化
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值**绑定(bind)**在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因而无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。
定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的:
refVal = 2; //把 2 赋给 refVal 指向的对象,此处即是赋给了 ival
int ii = refVal; // 与 ii = ival 执行结果一样
为引用赋值,实际上是把值赋给了与引用绑定的对象。获取引用的值,实际上是获取了与引用绑定的对象的值。同时,以引用作为初始值,实际上是以与引用绑定的对象作为初始值:
// 正确:refVal3 绑定到了那个与 refVal 绑定的对象上,这里就是绑定到 ival 上
int &refVal3 = refVal;
// 利用与 refVal 绑定的对象的值初始化变量 i
int i = refVal; // 正确:i 被初始化为 ival 的值
因为引用本身不是一个对象,所以不能定义引用的引用。
引用的定义
允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号 &
开头:
int i = 1024, i2 = 2048; // i 和 i2 都是 int
int &r = i, r2 = i2; // r 是一个引用,与 i 绑定在一起,r2 是 int
int i3 = 1024, &ri = i3; // i3 是 int, ri 是一个引用,与 i3 绑定在一起
int &r3 = i3, &r4 = i2; // r3 和 r4 都是引用
除了之后介绍的两种例外情况,其他所有引用的类型都要和与之绑定的对象严格匹配。而且,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
int &refVal4 = 10; // 错误:引用类型的初始值必须是一个对象
double dval = 3.14;
int &refVal5 = dval; // 错误:此处引用类型的初始值必须是 int 型对象
特殊情况一——const的引用
可以把引用绑定到 const
对象上,就像绑定到其他对象上一样,我们称之为对常量的引用(reference to const)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象上:
const int ci = 1024;
const int &r1 = ci; // 正确:引用及其对应的对象都是常量
r1 = 42; // 错误:r1 是对常量的引用
int &r2 = ci; // 错误:试图让一个非常量引用指向一个常量对象
因为不允许直接为 ci
赋值,当然也就不能通过引用去改变 ci
。因此,对 r2
的初始化是错误的。假设该初始化合法,则可以通过 r2
来改变它引用对象的值,这显然是不正确的。
初始化和对 const 的引用
在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式:
int i = 42;
const int &r1 = i; // 允许将 const int& 绑定到一个普通 int 对象上
const int &r2 = 42; // 正确:r2 是一个常量引用
const int &r3 = r1 * 2; // 正确:r3 是一个常量引用
int &r4 = r1 * 2; // 错误:r4 是一个普通的非常量引用
下面的语句也是正确的:
double dval = 3.14;
const int &ri = dval;
而下面的语句是不正确的:
double dval = 3.14;
int &ri = dval;
对 const 的引用可能引用一个并非 const 的对象
必须认识到,常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:
int i = 42;
int &r1 = i; // 引用 ri 绑定对象 i
const int &r2 = i; // r2 也绑定对象 i,但是不允许通过 r2 修改 i 的值
r1 = 0; // r1 并非常量,i 的值修改为 0
r2 = 0; // 错误:r2 是一个常量引用
r2
绑定(非常量)整数 i
是合法的行为。然而,不允许通过 r2
修改 i
的值。尽管如此,i
的值仍然允许通过其他途径修改,既可以直接给 i
赋值,也可以通过像 r1
一样绑定到 i
的其他引用来修改。
特殊情况二——类型转换与继承
我们可以将基类的指针或引用绑定到派生类对象上。例如,我们可以用 Quote&
指向一个 Bulk_quote
对象,也可以把一个 Bulk_quote
对象的地址赋给一个 Quote*
。(这里的 Bulk_quote
是 Quote
的子类)。
可以将基类的指针或引用绑定到派生类对象上有一层极为重要的含义:当使用基类的引用(或指针)时,实际上我们并不清楚该引用(或指针)所绑定对象的真实类型。该对象可能是基类的对象,也可能是派生类的对象。