高质量程序设计指南c++/c语言(30)--引用
引用就是对象的另一个名字,不能定义引用类型的引用。引用必须用与该引用同类型的对象初始化。
const引用是指向const对象的引用。
const int ival = 1024; const int &refVal = ival; //ok int &ref2 = ival; //error
严格的说,const引用指的是指向const对象的引用。const引用可以初始化为不同类型的对象或者右值,如字面常量:
int i = 10; //legal for const references only const int &r = 42; const int &r2 = r+i;
同样的初始化用于非const引用却是不合法的。其原因非常微妙,值得探究一下。观察将引用绑定到不同的类型时所发生的事情,最容易理解上述行为。假如我们编写
double dval = 3.14; const int &ri = dval;
编译器会把上述代码转化为一下形式:
int temp = dval; const int &ri = temp;
如果ri不是const,那么可以给ri赋一新值。这样做不会修改dval,而是修改了temp。期望对ri的赋值会修改dval的程序猿会发现dval的值并没有改变。仅允许const引用绑定到需要临时变量作为媒介来完全绑定过程的值,因为const引用是只读的。
非const引用只能绑定到与该引用同类型的对象。
const引用则可以绑定到不同但相关的类型的对象或者绑定到右值。
如果函数具有普通的非const引用形参,则显然不能通过const对象进行调用。毕竟,此时函数可以修改传递进来的对象,这样就违背了实参的const特性。但是比较容易忽略的是,调用这样的函数时,传递一个右值或者具有需要转换的类型的对象同样是不允许的。
int incr(int &val) { return ++val; } int main(void) { short v1 = 0; const int v2 = 42; int v3 = incr(v1); //error:v1 is not an int v3 = incr(v2); //error:v2 is const v3 = incr(0); //error:literals are not lvalues v3 = incr(v1 + v2); //error:addition does not yield an lvalue int v4 = incr(v3); // ok }
应该将不修改相应实参的形参定义为cosnt引用。如果将这样的形参定义为非const引用,则毫无必要的限制了该函数的使用。
int find_char(string &s, char c) { int i = 0; while(i != s.size() && s[i] != c) ++i; return i; }
虽然该函数没有修改s的值,但是这样的定义带来的问题是不能通过字符串字面值来调用这个函数:find_char("hello", 's');
虽然字面值可以转换为string对象,但上述调用仍然导致编译失败。
应该将不需要修改的引用形参定义为const引用,普通的非const引用形参在使用时太不灵活。这样的形参既不能用const对象初始化,也不能用字面值或者产生右值的表达式实参初始化。