C++中的引用(reference)
副标题#e#
1.简介
引用是C++引入的新语言特性。从语意上来说,引用就是一个变量的别名,就好象古代人的“字”和“号”,东坡居士和苏轼只是一小我私家的差异称号。对引用的操纵对变量发生的影响与对变量直接操纵完全一样。譬喻:
int i = 0;
int & iRef = i;
iRef++; // i = iRef = 1
尽量引用不利用指针的操纵符(*, ->)可是,它看上去跟指针好象并没有区别,并且就上面的例子而言,这个引用所发生的浸染完全可以由指针完成。那么为什么C++中还要增加这样一个特性呢?引用显然应该具备指针不能完成的成果,不然它就失去了代价。这方面的探讨我们留到第3节。
2.引用的语法
在这里我们只接头一些语法相关的问题。
·引用必需在界说的同时初始化
int i;
int & j; // 错误,没有初始化。
int & k=i; // 正确
这个例子有个很好的比喻,小时候小伴侣间会相互起“绰号”,这些绰号在发生的时候老是有所指的,即针对一个详细的小伴侣的。引用也一样,界说的时候,必需指明它是谁的别名。
·外部(extern)引用界说不必给出初值
extern int & i; // 正确,不必给出初值
·引用初始化后不能再使其成为其它变量的引用
int j, k;
int & i = j;
i = k; // 错误,不能变动!
引用雷同一个常量指针(int * const p),不能修改引用的指向。
·引用的地点
假设有如下界说:
int j;
int & i = j;
那么,&i应该是什么呢?是一个“引用的地点”么?谜底是:no。&i = &j,就是j这个变量的地点。
#p#副标题#e#
3.引用利用能力
3.1 引用和多态
引用是除指针外另一个可以发生多态结果的手段。这意味着,一个基类的引用可以指向它的派生类实例。譬喻:
class A;
class B: public A
{
…
};
B b;
A & aRef = b; // 基类引用指向派生类
假如A类中界说有虚函数,而且在B类中重写了这个虚函数,就可以通过aRef发生多态结果。
3.2 作为参数
引用的一个重要浸染就是作为函数的参数范例。C/C++的函数参数是传值的,假如有大工具(譬喻一个大的布局)需要作为参数通报的时候,以前的(C语言中)方案往往是指针,因为这样可以制止将整个工具全部压栈,可以提高措施的效率。可是此刻(C++中)又增加了一种同样有效率的选择,就是引用。
与指针范例的参数一样,引用岂论指向什么范例的工具,作为参数通报的时候都是只压栈4个字节(在32位机上)。引用所占用的4字节巨细是按照编译器发生的代码判定的,因为sizeof(a_reference)只能获得它所指向工具的巨细。
引用型参数应该在能被界说为const的环境下,只管界说为const,这不仅是让代码更结实,也有些其它方面的需要,譬喻,假设有如下函数声明:
string foo();
void bar(string & s);
那么下面的表达式将是犯科的:
bar(foo());
bar("hello world");
原因在于foo()和"hello world"串城市发生一个姑且工具,而在C++中,这些姑且工具都是const范例的。因此上面的表达式就是试图将一个const范例的工具转换为非const范例,这是犯科的。
3.3 作为返回值
引用作为返回值的时候,有一些法则必需遵守。这些法则包罗:
不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,措施会进入未知状态。
不能返回函数内部new分派的内存的引用。这条可以参照Effective C++[1]的Item 31。固然不存在局部变量的被动销毁问题,可对付这种环境(返回函数内部new分派内存的引用),又面对其它难过排场。譬喻,被函数返回的引用只是作为一个姑且变量呈现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分派)就无法释放,造成memory leak。
可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当工具的属性是与某种业务法则(business rule)相关联的时候,其赋值经常与某些其它属性可能工具的状态有关,因此有须要将赋值操纵封装在一个业务法则傍边。假如其它工具可以得到该属性的很是量引用(或指针),那么对该属性的纯真赋值就会粉碎业务法则的完整性。
别的,引用也经常与一些操纵符的重载相关:
#p#分页标题#e#
流操纵符<<和>>。这两个操纵符经常但愿被持续利用,譬喻:cout << "hello" << endl;因此这两个操纵符的返回值应该是一个仍然支持这两个操纵符的流引用。可选的其它方案包罗:返回一个流工具和返回一个流工具指针。可是对付返回一个流工具,措施必需从头(拷贝)结构一个新的流工具,也就是说,持续的两个<<操纵符实际上是针对差异工具的!这无法让人接管。对付返回一个流指针则不能持续利用<<操纵符。因此,返回一个流工具引用是独一选择。这个独一选择很要害,它说明白引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个观念的原因吧。
赋值操纵符=。这个操纵符象流操纵符一样,是可以持续利用的,譬喻:x = j = 10;可能(x=10)=100;赋值操纵符的返回值必需是一个左值,以便可以被继承赋值。因此引用成了这个操纵符的独一返回值选择。
在别的的一些操纵符中,却千万不能返回引用:
+-*/四则运算符。它们不能返回引用,Effective C++[1]的Item23具体的接头了这个问题。主要原因是这四个操纵符没有side effect,因此,它们必需结构一个工具作为返回值,可选的方案包罗:返回一个工具、返回一个局部变量的引用,返回一个new分派的工具的引用、返回一个静态工具引用。按照前面提到的引用作为返回值的三个法则,第2、3两个方案都被反对了。静态工具的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个工具了。
3.4 什么时候利用引用
此刻可以总结一下什么时候利用引用这个问题了。首先我们要看看什么时候必需利用引用:
流操纵符<<和>>、赋值操纵符=的返回值
拷贝结构函数的参数、赋值操纵符=的参数
其它下面的环境都是推荐利用引用,可是也可以不利用引用。假如不想利用引用,完全可以利用指针可能其它雷同的对象替代:
异常catch的参数表
大工具作为参数通报
返回容器类中的单个元素
返回类数据成员(非内建数据范例成员)
返回其它耐久存在的,且得到者不认真销毁的工具
别的一些环境下,不能返回引用:
+-*/四则运算符