当前位置: 首页 > 文档资料 > C++大学教程 >

3.17 引用与引用参数

优质
小牛编辑
139浏览
2023-12-01

许多编程语言中调用函数的两种方法是按值调用(call—by-value)和按引用调用(call—by—referrence)。参数按值调用传递时,生成参数值副本并传给被调用函数。副本的改变并不影响调用者的原始变量值,这样就可以防止意外的副作用影响开发正确、可靠的软件系统。本章前面的程序中
每个传递的参数都是按值调用传递的。

性能提示3.10
接值调用传递的一个缺点是,如果传递较大的数据项目,则复制这个数据可能要占用相当长的执行时间。

本节介绍引用参数,这是“+提供的两种按引用调用的方法之一。桉引用调用时,调用者让被调用函数能够直接访问调用者的数据,并允许被调用函数能够修改其中的数据。

性能提示3.11
按引用调用对性能有利,因为它消除了复制大量数据的开销。

软件工程视点3.18
按引用调用的安全性较差,因为被调用函数能够直接访问和修改调用者的数据。

我们将介绍如何利用按引用调用的性能优势同时又满足软件工程的要求(防止破坏调用者数据)。

引用参数是其相应的参数的别名。要表示函数的参数是按引用传递的,只要在函数原型中该参数类型后面加上&即可,在函数首部中列出参数类型时也要使用相同的规则。例如,函数首部中的下列声明:int &count

表示count是int的引用。在函数调用中,只要指定变量名,该变量就会通过引用传递。在被调用函数体中,通过参数名指定的变量实际上就是引用了调用函数中的原始变量,被调用函数可以直接修改原始变量。一般来讲,函数原型和函数首部必须相符。

图3.10比较按值调用、按引用调用与引用参数。调用 squareByValue 和 squareByReference 中,所用参数的形式是相同的,都是只指定名称。如果不检查函数原型或函数定义,要判断被调用函数是否修改了该参数是不可能的。由于函数原型是强制的,因此编译器能够顺利解决歧义性。

// Fig. 3.20: fig0320.cpp
// Comparing call-by-value and call-by-reference
// with references.
#include <iostream.h>
int squareByValue( int );
void squareByReference( int& );
int main(){
int x = 2, z = 4;
Cout << "x = "<< x << "before squareByValue\n"
<< "Value returned by squareByValue: "
<< squareByValue( x ) << endl
<< "x = "<< x << "after squareByValue\n" << endl;
cout << "z =" << z << "before squareByReference" << endl;
squareByReference( z );
cout << "z =" << z << "after squareByReference" << endl;
return 0;
}
int squareByvalue ( int a ){
return a *= a; // caller's argument not modified
}
void squareByReference( iht &cRef ){
cRef *= cRef; // caller's argument modified
}

输出结果:

x=2 before squareByValue
Value returned by squareByValue: 4
x = 2 after squareByValue

z = 4 before $quareByReference
z = 16 after squareSyReference

图 3.20 按引用调用的举例

常见编程错误3.26
由于引用参数在被调用函数体中只指定名称.因此程序员可能把引用参数当作按值调用的参数。这样,如果调用函数改变变量原始副本,则可能产生预想不到的副作用。

第5章介绍指针时,将会介绍指针提供另一种形式的引用调用,调用样式能明确表示按引用调用(可能修改调用者的参数)。

性能提示3.12
如果要传递较大的对象,用常量引用参数模拟按值调用的情况.避免传递较大对象副本的开销。

要指定引用常量,在参数声明的类型说明符前面加上const限定符。
注意叫 squareByReference 函数参数表中&的位置。有些C++程序员喜欢写成int& cRef而不是int &cRef。

软件工程视点3.19
为了获得程序的清晰性和高性能,许多C++程序员喜欢通过指针将可修改参数传道给函数,不可修改的小参数按值调用传递,而不可修改的大参数用常量引用传递给函数。

引用也可以用作函数中其他变量的别名。例如下列代码:

int count = 1 // declare integer variable count
int &cRef = count; // create cRef as an alias for count
++cRef; // increment count (using its alias)

用别名 eRef 递增变量 count 的值。引用变量应在声明中初始化(见图3.21和图3.22),不能作为其他变量的别名而重新赋值。将引用声明为另一变量的别名后,对该别名(即引用)进行的所有操作实际上是对原始变量本身进行的,别名只是原始变量的另一个名称。获得引用地址和比较引用不会造成语法错误,而且每个操作实际上是对原始变量本身进行的。引用参数应为左值,而不能是常量或返回左值的表达式。

常见编程错误3.27
在一条语句中声明多个引用时会出现一些常见问题。例如,要声明x、y、z变量为整数的引用,表达式

int& x=a,y=b,z=c 或 int& x,y,z;都是错误的。正确的应是 int &xa, &y=b, &z=c;。

函数可以返回引用,但却会经常出现问题。函数返回被调用函数中声明的变量的引用时,变量应在函数中声明为static,否则引用指的是函数终止时删除的动态变量,这个变量是未定义的,程序的执行情况将无法预测(有些编译器会对此发出警告)。引用未定义变量称为悬挂引用(danglingreference)。

常见编程错误3.28
声明引用变量而不对其进行初始化是个语法错误。

// References must be initialized
#include <iostream.h>
int main(){
int x = 3, &y = x; // y is now an alias for x
cout << "x =" << x << endl << "y =" << y << endl;
y=7;
cout << "x -" << x << e.dl << "y -" << y << endl;
retur. 0;
}

输出结果:

x = 3
y = 3
x = 7
y = 7

图 3.21 使用初始化的引用

// References must be initialized
#include <iostream.h>
int main()(
int x = 3, &y; // Erroz: y must be initialized
cout << "x =" << x << endl << "y = "<< y << endl;
y = 7;
cout << "x =" << x << endl << "y -" << y << endl;
return 0;
}

输出结果:

Compiling FIG0321.CPP:
Error FIG03_21,CPP 6:Reference variable 'y' must be
initialized

图 3.22 使用未初始化的引用

常见编程错误3.29
将前面声明的引用重新变为另一变量的别名是个逻辑错误,只是将已经是别名的引用的地址赋给另一变量。

常见编程错误3.30
被调用函数中返回自动变量的指针或引用是个逻辑错误。有些编译器会对此发出警告。