return0;//没有此句会有警告。
最好加上此句
}
1.3.3函数原型声明
在C++中,如果函数调用的位置在函数定义之前,则要求在函数调用之前必须对所调用的函数作函数原型声明,这是强制性的。
如有定义:
intmax(inta,intb)
{returna+b;
}
声明时必须写成:
intmax(inta,intb);或
intmax(intx,inty);或
intmax(int,int);
1.3.4函数的重载
1、为什么要用重载函数
在C中,在同一作用域中不能有同名的函数,每个函数必须有其唯一的名字,这样有时会令人生厌。
例如,求一个数的绝对值,由于要求命名唯一,所以对于不同的类型需要不同名字的函数:
intabs(int);返回整型参数的绝对值
longlabs(long)返回长整型参数的绝对值
doublefabs(double)返回双精度参数的绝对值
极其相似操作函数我们却起三个不同的名字,这样子不是很好管理,所以C++为了方便程序员编写程序特别引入了函数重载的概念来解决此问题。
C++允许在同一作用域用同一函数名定义多个函数,这些函数的参数个数和参数类型不相同,这些同名的函数用来实现不同的功能。
这就是函数的重载。
重载是用来描述同名函数具有相同或者相似功能,但数据类型或者是参数不同的函数管理操作的称呼。
同一个函数名可以用来代表不同功能的函数,也就是一名多用。
这几个函数所做的事情是一样的都是求绝对值。
因此,使用三个不同的函数名,看上去很笨拙,若给以同样的名字就会方便得多。
这就是重载技术。
这种技术在C++中早已用于基本数据类型运算,如加法只有一个名字+,但它可以用来加整数值、浮点值和指针值。
插入运算符“<<”和提取运算符“>>”既可以左移和右移也可以是输入输出运算符。
例如,上述3个函数的声明可以改为:
intabs(int);
longabs(long);
doubleabs(double);
C++用一种函数命名技术可以准确判断出应该使用哪个abs()函数。
例如:
abs(-10);//调用intabs(int);
abs(-1000000);//调用longabs(1ong);
abs(-12.23);//调用doubleabs(double);
2、匹配重载函数的顺序
在调用一个重载函数f()时,编译器必须搞清函数名f究竟是指哪个函数。
这是靠将实参类型和所有被调用的f()函数的形参类型一一比较来判定的。
按下述3个步骤的先后顺序找到并调用那个函数:
(1)寻找一个严格的匹配,如果找到了,就用那个函数。
(2)通过内部转换寻求一个匹配,只要找到了,就用那个函数。
(3)通过用户定义的转换寻求一个匹配,若能查出有唯一的一组转换,就用那个函数 例如,重载函数print()的匹配:
voidprint(double);
voidprint(int);
voidfunc()
{
print
(1);//匹配voidprint(int);
print(1.0);//匹配voidprint(double);
print('a');//匹配voidprint(int);
print(3.1415f);//匹配voidprint(double);
}
例如,对于重载函数print()声明,其下面的函数调用将引起错误:
voidprint(1ong);
voidprint(double);
voidfunc(inta)
{ print(a);//error:
因为有二义性
}
3、使用说明
(1)C++的函数如果在返回类型、参数类型、参数个数、参数顺序上有所不同,则认为是不同的。
但重载函数如果仅仅是返回类型不同,则是不够的。
例如,下面的声明是错误的:
voidfunc(int);
intfunc(int);
编译器无法区分函数调用"func(3)”是指上述哪一个重载函数。
因此重载函数至少在参数个数、参数类型或参数顺序上有所不同。
(2)typedef定义的类型只能使之相同于一个已存在的类型,而不是建立新的类型,所以不能用typedef定义的类型名来区分重载函数声明中的参数。
例如,下面的代码实际上是同一个函数:
typedefINTint;
voidfunc(intx){//...}
voidfunc(INTx){//...)//error:
函数重复定义
编译器不能区分这两个函数的差别,INT只不过是int的另一种称呼而已;
(3)让重载执行不同的功能,是不好的编程风格。
同名函数应该具有相同的功能。
如果定义一个abs()函数而返回的却是一个数的平方根,该程序的可读性受到破坏。
下面我们用重载的概念来编写一个求3个数(分别是整数、实数、长整数的情况中最大数的程序。
P12例1.6
#include
usingnamespacestd;
intmax(inta,intb,intc)
{
if(b>a)
a=b;
if(c>a)
a=c;
returna;
}
floatmax(floata,floatb,floatc)
{
return(a>b?
a:
b)>c?
(a>b?
a:
b):
c;
}
longmax(longa,longb,longc)
{
intt;
if(a
t=a,a=b,b=t;
if(at=a,a=c,c=t;
returna;
}
intmain()
{
inta,b,c;
floatd,e,f;
longg,h,i;
cin>>a>>b>>c;
cin>>d>>e>>f;
cin>>g>>h>>i;
cout<cout<cout<}
上例是函数的参数个数相同,但类型不同。
系统会根据实参的类型来匹配。
还可以参数个数不同,请看下例。
P13例1.7用一个函数求2个正整数或3个正整数的最大者。
#include
usingnamespacestd;
intmax(inta,intb)
{
returna>b?
a:
b;
}
intmax(inta,intb,intc)
{
return(a>b?
a:
b)>c?
(a>b?
a:
b):
c;
}
intmain()
{
inta,b,c;
cin>>a>>b>>c;
cout<}
1.3.5函数模板
函数的重载可以实现一个函数名多用,将功能相同或类似的函数用同一个函数名来定义。
这样使编程者在调用同类函数时感到含义清楚,方法简单。
但是在程序中仍然要分别定义每一个函数。
例如:
求3个数中最大的数(分别考虑整数、双精度数、浮点数的情况)
#include
usingnamespacestd;
intmain()
{
intmax(inta,intb,intc);
doublemax(doublea,doubleb,doublec);
floatmax(floata,floatb,floatc);
inti1,i2,i3,i;
i1=10;i2=23;i3=13;
i=max(i1,i2,i3);
cout<<"i_max"<
doubled1,d2,d3,d;
d1=23,5;d2=45;d3=56.4;
d=max(d1,d2,d3);
cout<<"d_max"<floatf1,f2,f3,f;
f1=34.3;f2=65.4;f3=56.4;
f=max(f1,f2,f3);
cout<<"f_max"<}
intmax(inta,intb,intc)
{if(a
a=b;
if(aa=c;
returna;
}
doublemax(doublea,doubleb,doublec)
{if(a
a=b;
if(aa=c;
returna;
}
floatmax(floata,floatb,floatc)
{if(a
a=b;
if(aa=c;
returna;
}
从上面的程序中可以看出,有3个max函数的函数体是完全相同的,只是形参的类型不同,也要分别定义,能否对此进行简化呢?
为了解决这个问题,C++提供了函数模板。
所谓函数模板就是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。
这个通用函数就称为函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。
在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
我们将上面的例子用函数模板来实现
#include
usingnamespacestd;
template//模板声明,其中T为类型参数,会有不同数据类型的地方,
//以设定类型的变量替代。
Tmax(Ta,Tb,Tc)//定义一个通用函数,用T作虚拟的类型名
{
if(a
a=b;
if(aa=c;
returna;
}
intmain()
{inti1,i2,i3,i;
i1=10;i2=23;i3=13;
i=max(i1,i2,i3);
cout<<"i_max"<
doubled1,d2,d3,d;
d1=23,5;d2=45;d3=56.4;
d=max(d1,d2,d3);
cout<<"d_max"<floatf1,f2,f3,f;
f1=34.3;f2=65.4;f3=56.4;
f=max(f1,f2,f3);
cout<<"f_max"<}
从上例可以看出,用函数模板比函数重载更为方便,程序简洁多了。
但应注意它只适用于函数体相同、函数的参数个数相同而类型不同的情况,如果参数的个数不同,则不能用函数模板。
例:
定义一个交换两变量值的模板函数,利用它来实现整数、浮点数、双精度数、字符串的交换。
(学生练习)
#include
#include
usingnamespacestd;
template//模板声明,其中T为类型参数
voidswap(T*a,T*b)//定义一个通用函数,用T作虚拟的类型名
{
T*temp;
*temp=*a;*a=*b;*b=*temp;
}
intmain()
{
inti1,i2;
i1=10;i2=23;
swap(i1,i2);
cout<<"i1="<doubled1,d2;
d1=23,5;d2=45;
swap(d1,d2);
cout<<"d1="<floatf1,f2;
f1=34.3;f2=65.4;
swap(f1,f2);
cout<<"f1="<strings1,s2;
s1="good";s2="better";
swap(s1,s2);
cout<<"s1="<}
模板函数调用时,类型实参与类型形参的匹配要求很苛刻。
下面来看一个实例。
#include
#include
usingnamespacestd;
template//模板声明,其中T为类型参数
voidswap(T*a,T*b)//定义一个通用函数,用T作虚拟的类型名
{
T*temp;
*temp=*a;*a=*b;*b=*temp;
}
voidf(doublea,doubleb)
{
return;
}
intmain()
{
intia=3;
doubledb=5.0;
f(ia,db);//okia的类型与double不同,但可以隐式转换为double,实现合法调用
swap(ia,db);//erroria与db的类型不同,不能统一到同一个类型上。
//模板类型参数没有隐式转换的说法,必须精确匹配,所以编译错误
}
1.3.6有默认参数的函数
1、默认参数的目的
C++可以给函数定义默认参数值。
通常,调用函数时,要为函数的每个参数给定对应的实参。
例如:
voiddelay(intloops);//函数声明
voiddelay(intloops)//函数定义
{if(1oops==0)
return;
for(inti=0;i }
无论何时调用delay()函数,都必须给loops传一个值以确定时间。
但有时需要用相同的实参反复调用delay()函数。
C++可以给参数定义默认值。
如果将delay()函数中的loops定义成默认值1000,只需简单地把函数声明改为:
voiddelay(intloops=1000);
这样,无论何时调用delay()函数,都不用给loops赋值,程序会自动将它当作值1000进行处理。
例如,调用:
delay(2500);//loops设置为2500
delay();//ok:
loops采用默认值1000
调用中,若不给出参数,则按指定的默认值进行工作。
允许函数默认参数值,是为了让编程简单,让编译器做更多的检查错误工作。
2、默认参数的声明
默认参数在函数声明中提供,当又有声明又有定义时,定义中不允许默认参数。
如果函数只有定义,则默认参数才可出现在函数定义中。
例如:
voidpoint(int=3,int=4);//声明中给出默认值
voidpoint(intx,inty)//定义中不允许再给出默认值
{cout< cout< }
3、默认参数的顺序规定
如果一个函数中有多个默认参数,则形参分布中,默认参数应从右至左逐渐定义。
当调用函数时,只能向左匹配参数。
例如:
voidfunc(inta=1,intb,intc=3,intd=4);//error
voidfunc(inta,intb=2,intc=3,intd=4);//ok
voidfunc(inta,intb,intc=3,intd=4);//ok
voidfunc(inta,intb,intc,intd=4);//ok
请看下例:
#include
usingnamespacestd;
voidf1(inta,intb=2,intc=3,intd=4);
voidf2(inta,intb,intc=3,intd=4);
intmain()
{
f1(30);
f1(30,40);
f1(30,40,50);
f1(30,40,50,60);
f2(300,400);
f2(300,400,500);
f2(300,400,500,600);
return0;
}
vo