|
Chapter 8 函数,第二部分:引用,重载和默认参数
深入
声明引用参数
当Bjarne Stroustrup在1986年写The C++ Programming Language(在书中他第一次描述了C++)一书时,他介绍了一种声明引用参数的格式,其他一些程序员也采用了这种格式。在这种格式中,&是和类型名而不是变量名放在一起的。例如,下面是声明函数swap()原型的另一种方法:
void swap(int& x, int& y);
你可以看到,&是与类型名int而不是变量名x相邻的。
另外,一些程序员在声明指针时,也把*与类型名相连而不是与变量名相连,如下所示:
float* p;
这种声明反映出,一些程序员希望C++能包含一个独立的引用类型或者指针类型。但是,将&或*与类型相连而不是和变量相连会产生一个问题,根据正式的C++语法,&和*都不能作用于一组变量,所以这会产生令人迷惑的声明。例如,下面的声明创建了一个(而不是两个)整型指针:
int* a, b;
在上面的代码中,b被声明为整数(而不是指向整数的指针),因为C++语法规定,在声明变量时,*和&都是作用于紧跟其后的变量,而不是变量类型。
有一点很重要,就C++编译器而言,无论你写成int *p或int* p都无关紧要。也就是说,如果你更愿意将*或&与类型相连而不是与变量相连,也是可以的。但是,为了避免产生混淆,本书将继续把*和&与它们所修饰的变量名连在一起。
提示:C不支持引用。也就是说,在C中创建引用调用的惟一方法是使用指针,在函数swap()第一个版本中使用的就是这种方法。当把C代码转换为C++代码时,你要尽可能地将这种参数转换为引用参数。
8.2.1 返回引用
函数可以返回引用。在C++程序设计中,有几种返回引用值的用法。在本书后面学习到运算符重载时你将会看到这样的用法。然而,返回引用还有其他重要的应用,现在你就会使用到这些应用。
当函数返回引用时,它返回的是一个指向返回值的隐式指针。这带来了一种可能性:函数可以被放在赋值语句的左边!例如下面这个简单的程序:
//返回一个引用
#include <iostream>
using namespace std;
double &f();
double val = 100.0;
int main()
{
double newval;
cout << f() << '\n'; //输出val的值
newval = f(); //将val的值赋给newval
cout << newval << '\n'; //输出newval的值
f() = 99.1; //改变val的值
cout << f() << '\n'; //显示 val的新值
return 0;
}
double &f()
{
return val; //返回一个指向val的引用
}
函数输出如下:
100
100
99.1
让我们来仔细研究这个程序。在开始,f()被声明为返回一个double类型的引用,全局变量val被初始化为100。接下来,下面的语句将输出val的初始值:
cout << f() << '\n'; //输出val的值
当调用f()时,函数将返回一个指向val的引用。因为f()被声明为返回引用,所以下面这行语句:
return val; //返回一个指向val的引用
将自动地返回一个指向val的引用。这个引用然后被cout语句用来输出val的值。
在下面这行语句:
newval = f(); //将val的值赋给newval
由f()返回的指向val的引用被用来将val的值赋给newval。
在该程序中我们最感兴趣的是下面这行语句:
f() = 99.1; //改变val的值
这条语句使得val的值被变成99.1。原因如下:由于f()返回的是val的引用,而该引用则成为赋值语句的目标变量。因此,99.1通过由f()所返回的引用被间接地赋给了val。
最后,在下面这行语句中:
cout << f() << '\n'; //输出val的新值
当在cout语句中使用了由f()返回的val引用时,val的新值就被输出。
下面是使用返回引用类型的另一个示例:
#include <iostream>
using namespace std;
double &change_it(int i); //返回一个引用
double vals[] = {1.1, 2.2, 3.3, 4.4, 5.5};
int main()
{
int i;
cout << "Here are the original values: ";
for(i=0; i<5; i++)
cout << vals[i] << ' ';
cout << '\n';
change_it(1) = 5298.23; //改变第2个元素的值
change_it(3) = -98.8; //改变第4个元素的值
cout << "Here are the changed values: ";
for(i=0; i<5; i++)
cout << vals[i] << ' ';
cout << '\n';
return 0;
}
double &change_it(int i)
{
return vals[i]; //返回一个对第i个元素的引用
}
该程序改变了vals数组中第二个和第四个元素的值。
程序输出如下:
Here are the original values: 1.1 2.2 3.3 4.4 5.5
Here are the changed values: 1.1 5298.23 3.3 -98.8 5.5
让我们来看看这是如何实现的。函数change_it()被声明为返回double类型的引用,该函数返回的是一个由其参数i指定的数组vals中元素的引用。因此,在main()中,当下面的语句被执行时:
change_it(1) = 5298.23; //改变第2个元素的值
change_it()返回的是对vals[1]的引用。通过这个引用,vals[1]被赋值为5298.23。当下面的语句被执行时,同样,vals[3]被赋值为-98.8:
change_it(3) = -98.8; //改变第4个元素的值
因为change_it()返回的是数组vals中特定元素的引用,所以该函数可以放在赋值语句的左边用来对这个数组元素进行赋值。
当函数返回一个引用时,要注意被引用的对象不能超出作用域,例如下面这个函数:
//错误, 不能返回对局部变量的引用。
int &f()
{
int i=10;
return i;
}
在上述代码中,当函数f()返回时,局部变量i将超出作用域。因此,由f()返回的对i的引用将是未定义的引用。事实上,一些编译器正是因为这个原因,在编译f()函数时认为它是不可写的(也就是不能放在赋值语句的左边)。此外,这类问题也可能会间接产生,所以当你返回对一个对象的引用时,要仔细检查这个对象是否会超出作用域。
|
|