5 带缺省参数的函数
一般情况下,实参个数应与形参个数相同。C++允许实参个数与形参个数不同。办法是在形参表列中对一个或几个形参指定缺省值(或称默认值)。例如某一函数的首部可用如下形式:
void fun(int a, int b,int c=100)
在调用此函数时如写成fun(2,4,6),则形参a,b,c的值分别为2,4,6(这是与过去一样的)。如果写成fun(2,4),即少写了最后一个参数,由于在函数定义时已指定了c的缺省值为100,因此a,b,c的值分别为2,4,100。请注意:赋予缺省值的参数必须放在形参表列中的最右端。例如:
void f1(float a, int b, int c=0, char d='a') (正确)
void f2(float a, int c=0, char d='a', int b) (不正确)
利用这一特性,可以使函数的使用更加灵活。例如例14.4求两个数或3个数中的最大数。也可以不用重载函数,而将函数max的首行写成
int max(int a, int b,int c=-32768)
如果只想从两个数中找大者,则可以在调用时写成max(100,675),c的值自动取 -32768,由于一32768是最小整数,因此从100,675,-32768中选大者和从100,675中选大者的结果是一样的。
注意:不要同时使用重载函数和缺省参数的函数,因为当调用函数时少写一个参数,系统无法判定是利用重载函数还是利用缺省参数的函数,会发生错误。
6变量的引用类型
6.1 引用的概念
“引用”(reference)是C++的一种新的变量类型,是对C的一个重要扩充。它的作用是为一个变量起一个别名。假如有一个变量a,想给它起一个别名b,可以这样写:
int a;
int&b=a:
这就声明了b是a的“引用”,即a的别名。经过这样的声明后,使用a或b的作用相同,都代表同一变量。注意:在上述声明中,&是“引用声明符”,并不代表地址。不要理解为“把 a的值赋给b的地址”。声明引用并不另开辟内存单元,b和a都代表同一变量单元。在声明一个引用型变量时,必须同时使之初始化,即声明它代表哪一个变量,在声明一个变量的引用后,在本函数执行期间,该引用一直与其代表的变量相联系,不能再作为其他变量的别名。下面的用法不对:
int al,a2;
int&b=a1;
int&b=a2; (企图使b变成a2的引用(别名)是不行的)
6.2 引用的简单使用
通过下面的例子可以了解引用的简单使用。
例14.5 了解引用和变量的关系。
# include <iostream.h>
# include <iomanip.h>
void main( )
{inta=10;
int &b=a; //声明b是a的引用
a=a*a; //a的值变化了,b的值也应一起变化
cout<<a<<setw(6)<<b;
b=b/5; //b的值变化了,a的值也应一起变化
cout<<b<<setw(6)<<a;
}
a 的值开始为10,b是a的引用,它的值当然也应该是10,当a的值变为100(a*a的值)时,b
的值也随之变为100。在输出a和b的值后,b的值变为20,显然a的值也应为20(见图14.1)。运行记录如下:
100 100
- 20
6.3 引用作为函数参数
有了变量名,为什么还需要一个别名呢?C++之所以增加“引用”,主要是把它作为函数参数,以扩充函数传递数据的功能。
在C语言中,函数的参数传递有以下两种情况。
(1)将变量名作为实参。这时传给形参的是变量的值。传递是单向的,在执行函数期间形参值发生变化并不传回给实参,因为在调用函数时,形参和实参不是同一个存储单元。下面的程序无法实现两个变量的值互换。
例14.6错误的程序。
# include <iostream.h>
void swap(int a, int b)
{int temp;
temp=a;
a=b;
b=temp; //实现a和b的值互换
}
void main( )
{int i=3,j=5;
swap(i,j);
cout<<i<<","<<j<<endl; //i和j的值未互换
}
输出i和j的值仍为3和5。见图14.2示意。图14.2(a)表示调用函数时的数据传递,图14.2(b) 是执行swap函数体后的情况,a和b值的改变不会改变i和j的值。
为了解决这个问题,在第10章介绍了传递变量地址的方法。
- 传递变量的指针。使形参得到一个变量的地址,这时形参指针变量指向实参变量单元。程序见例14.7。
例14.7 使用指针变量作形参,实现两个变量的互换。
# include <iostream.h>
void swap(int* p1,int *p2)
{int temp;
temp=*pl;
*p1=*p2;
p2=temp;
}
void main( )
{int i=3,j=5;
swap(&i.&j);
cout<<i<<"."<<j<<endl;
}
形参与实参的结合见图14.3示意。调用函数时把变量;和j的地址传送给形参p1和 p2(它们是指针变量),因此*p1和i为同一内存单元,*p2和j为同一内存单元,图14.3(a)表示刚调用swap函数时的情况,图14.3(b)表示执行完函数体语句时的情况,显然,i和j的值改变了。
这种方法其实也是采用“值传递”方式,向一个指针变量传送一个地址。然后再通过指针变量访问有关变量。这样做能得到正确结果,但是在概念上“兜了一个圈子”,不那么直截了当。在PASCAL语言中有“值形参”和“变量形参”(即var形参),对应两种不同的传递方式,前者采用值传递方式,后者采用地址传递方式(传送的是变量的地址而不是变量的值,使形参指向一个变量)。在C语言中,只有“值形参”而无“变量形参”,全部采用值传递方式。C+十把引用型变量作为函数形参,就弥补了这个不足。
C++提供了向函数传递数据的第三种方法,即传送变量的别名。
例14.8 利用“引用形参”实现两个变量的值互换。
#include <iostream.h>
void swap (int &a,int &b)
{int temp;
temp=a;
a=b;
b=temp;
}
void main( )
{inti=3,j=5;
swap(i,j);
cout<<"i="<<i<<""<<j<<" <<endl;
}
输出结果为 i=5 j=3
在swap函数的形参表列中声明变量a和b是整型的引用变量(和其他变量一样,既可以在函数体中声明变量的类型,也可以在定义函数时在形参表列中声明变量的类型)。请注意:在此处&a 不是“a 的地址”,而是指“a是一个引用型变量”。但是此时并未对它们初始化,即未指定它们是哪个变量的别名。当 main 函数调用swap函数时由实参把变 量名传给形参。i的名字传给引用变量a,这样a就成了i的别名。同理,b成为j的别名。a和i代表同一个变量,b和j代表同一个变量。在swap函数中使a和b的值对换,显然,i和j的值同时改变了(见图14.4示意,其中(a)是刚开始执行swap函数时的情况,(b)是执行完函数体语句时的情况)。在main函数中输出i和j已改变了的值。
实际上,实参传给形参的是变量的地址,也就是使形参a具有变量i的地址,从而使a和i共享同一单元。为便于理解,我们说把变量i的名字传给引用变量a,使a成为i的别名。请注意这种传递方式和使用指针变量作形参时有什么不同?分析例14.8(对比例14.7),可以发现:①不必在swap函数中设立指针变量,指针变量要另外开辟内存单元、其内容是地址。而引用变量不是一个独立的变量,不单独占内存单元,在本例中其值为一整数。②在main函数中调用swap函数时实参不必在变量名前加&.以表示地址。这种传递方式相当于PASCAL语言中的“变量形参”,系统传送的是实参的地址而不是实参的值。显然,这种用法比使用指针变量简单、直观、方便。
的前面有类型符时(如int&a),它必然是对引用的声明;如果前面无类型符(如&a),则是取变量的地址。
7内置函数
调用函数时需要一定的时间,如果有的函数需要频繁使用,则所用时间会很长,从而降低程序的执行效率。C++提供一种提高效率的方法,即在编译时将所调用函数的代码嵌人到主调函数中。这种嵌入到主调函数中的函数称为内置函数(inline function),又称内嵌函数。在有些书中把它译成“内联函数”。
指定内置函数的方法很简单,只需在函数首行的左端加一个关键字inline即可。
例 14.9 将函数指定为内置函数。
# include <iostream.h>
inline int max(int a,int b,int c) //这是一个内置函数,求3个整数中的最大者
{ if(b>a)a=b;
if(c>a)a=c;
return a;
}
void main( )
{int i=7,j=10,k=25,m;
m=max(i,j,k):
cout<<" max="<<m<<endl;
}
由于在定义函数时指定它为内置函数,因此编译系统在遇到函数调用max(i,j,k)时,就用max函数体的代码代替max(i,j,k),同时将实参代替形参。这样,m=max(i,j, k)就被置换成
if(>i)i=j;
if(k>i)i=k;
m=it
内置函数与宏替换有些相似,但不完全相同。宏替换是在编译前由预处理程序进行预处理.它只作简单的字符替换而不作语法检查。而内置函数是在编译时处理的,编译程序能识别内置函数,对它进行语法检查。有些问题既可以用宏来处理,也可以用内置函数处理,显然,内置函数优于宏替换,它不会出现宏替换中可能产生的副作用。
使用内置函数可以节省运行时间,但却增加了目标程序的长度。假设要调用10次 max函数,则在编译时先后10次将max的代码复制并插人main函数,大大增加了main函数的长度。因此只用于规模很小而使用频繁的函数,可大大提高运行速度。