第 2章 函数第 2章 函 数
2.1 函数的定义与使用
2.2 函数调用机制
2.3 递归函数
2.4 默认参数的函数
2.5 内联函数
2.6 函数重载
2.7 函数模板
2.8 使用 C++系统函数第 2章 函数
2.1 函数的定义与使用在编辑一个大型程序时,即使各个函数的前后顺序不同,程序执行的开始点永远是主函数。主函数按照调用与被调用关系调用子函数。子函数如果与其它子函数又存在调用与被调用关系,当然还可以再调用其它子函数。
第 2章 函数在一对调用与被调用关系中,我们把调用其它函数的函数称为主调函数,被其它函数调用的函数称为被调函数 。 在一个较为复杂的大型程序中,一个函数很可能同时扮演两种不同的角色 ——主调函数与被调函数,即既调用别的函数 ( 被调函数 ) 又被另外的函数
( 主调函数 ) 调用 。 函数一般应遵守先定义后调用的原则,否则应在调用函数中先进行原形说明 。
第 2章 函数
2.1.1 函数的定义一个完整的函数定义由两部分组成,即函数头与函数体 。
1,函数定义的一般语法形式
<类型标识符 > <函数名说明符 >(形式参数表 )
{
说明性语句序列;
实现函数功能的语句系列;
}
第 2章 函数函数头是指上述格式中的 <类型标识符 > <函数名说明符 >( 形式参数表 ) 。 其中函数名可由函数设计者命名,可以是任何一个不重复的合法的标识符 ( 唯一的例外是,主函数必须命名为 main) 。
函数体是指上述格式中被一对大括号括起的复合语句部分 。 该函数所应实现的功能由相应的复合语句完成 。
第 2章 函数
2,函数的类型和返回值函数头部分的类型标识符规定了函数的返回值类型 。 函数的返回值是返回给主调函数的处理结果,由函数体部分的 return语句带回 。 例如,return value1。
无返回值的函数其类型标识符为 void,不必有
return语句 。
第 2章 函数
3,形式参数函数头部分的形式参数 (简称形参 )表的内容如下:
类型 l 形参名 1,类型 2 形参名 2,...,类型 n 形参名 n其中类型 1,类型 2,...,类型 n是类型标识符,表示形参的数据类型 ( int,double,float,char,bool等 ) ;形参名 1,形参名 2,...,形参名 n是形参名 ( 合法的自定义标识符 ) 。 形参是用来实现主调函数与被调函数之间的数据联系,通常将函数所处理的数据,影响函数功能的因素或者函数处理的结果作为形参 。 对于无形参的函数,其形参表的内容应该为空,但代表函数的小括号对不能省略 。
第 2章 函数函数在没有被调用的时候其形参只是一个符号,
它标志着在形参出现的位置应该有一个什么类型的数据 。 函数在被调用时才由主调函数将实际参数 (简称实参 )赋予形参 。 从这一点上说,C++中的函数与数学中的函数概念极其相似 。 例如,我们都熟悉的如下数学中的函数形式:
f(x)=3x2+5x-2
这个函数只有当自变量 x被赋以确定的值以后,才能计算出函数的值 。
第 2章 函数
2.1.2 函数的调用如果没有遵守先定义后调用的原则,调用函数之前先要在主调函数中声明函数原型 。 在主调函数中,
或所有函数之前,按如下形式进行函数原型声明:
<类型标识符 > <被调函数名 >(含类型说明的形参表 );
第 2章 函数如果是在所有函数之前声明了函数原型,那么该函数原型在本程序文件中任何地方都有效,也就是说,
在本程序文件中任何地方都可以依照该原型调用相应的函数 。 如果是在某个主调函数内部声明了被调函数原型,那么该原型就只能在这个函数内部有效 。
声明了函数原型之后,便可以按如下形式调用子函数:
<函数名 >(实参 1,实参 2,…,实参 n)
第 2章 函数实参列表中应给出与函数原型中形参个数相同,
类型相符的实参,每个实参都可以是常量,变量或表达式三者之一 。 实参与实参之间用逗号作为分隔符 。
注意,这里的逗号不是顺序求值运算符 。 函数调用可以作为一条语句,这时函数可以没有返回值 。 函数调用也可以出现在表达式中,这时就必须有一个明确的返回值 。 函数调用示例如下 。
第 2章 函数
【 例 2-1】 编写一个函数,把华氏温度转换为摄氏温度,公式为 C=(F-32)*5/9,公式中 F代表华氏温度,C
代表摄氏温度 。 在主函数中提示用户输入一个华氏温度,并完成输入及输出,由函数完成转化功能 。
程序代码如下:
#include <iostream.h>
float hstoss(float fHuashi);//原型说明
void main( )
{
第 2章 函数
float fHuashi;
cout<<endl<<"输入一个华氏温度值,";
cin>>fHuashi;
cout<<"华氏 "<<fHuashi
<<"度对应摄氏温度 "<<hstoss(fHuashi)<<"度 "<<endl;
//函数调用作为一个表达式出现在输出语句中
}
float hstoss(float fHuashi)
{
float fSheshi;
fSheshi = (fHuashi-32)*5/9;
return(fSheshi);
第 2章 函数
}
程序运行结果为输入一个华氏温度值,68
华氏 68度对应摄氏 20度第 2章 函数
【 例 2-2】 编写一个求 x的 n次方的函数 。
分析:求 x的 n次方,实际是求 x自乘 n次的乘积 。
程序代码如下:
#include <iostream.h>
double power(double dDishu,int iMi); //原型说明
void main( )
{
cout<<"底数 1.8 的 3 次幂是 "<<power(1.8,3)<<endl;
//函数调用作为一个表达式出现在输出语句中第 2章 函数
}
double power(double dDishu,int iMi)
{
int iCount;
double dResult = 1.0;
for(iCount=1;iCount<=iMi;iCount++)
dResult = dResult*dDishu;
return(dResult);
}
程序运行结果为底数 1.8 的 3 次幂是 5.832
第 2章 函数
【 例 2-3】 输入一个 8位的二进制数,将其转换为十进制数后再输出 。 对于非法输入 (除 0和 1以外的任何字符 )应给出提示信息 。
分析:将二进制数转换为十进制数,只要将二进制数的每一位乘以该位的权,然后相加 。 例如,
110100112=1(27)+ 1(26)+ 0(25)+ 1(24)+ 0(23)+ 0(22)
+ 1(21)+ l(20)=21110,所以,如果输入 00001101,则应输出 13。
第 2章 函数可以直接引用例 2-2中的函数 power来求 2的各次方 。
程序代码如下:
#include <iostream.h>
double power(double dDishu,int iMi); //函数原型说明
void main( )
{
int iCount=8;
int iValue=0;
char cChar;
bool bFlag=true;
第 2章 函数
cout<<"输入一个 8位的二进制数,";
while(iCount>0)
{
cin>>cChar;
if(cChar!='1'&&cChar!='0')
{
cout<<"这不是一个二进制数 !不能正确转换 "<<endl;
bFlag=false;
}
if(cChar=='1')
iValue=iValue+int(power(2,iCount-1));
第 2章 函数
iCount--;
}
if(bFlag)
cout<<"十进制值为,"<<iValue<<endl;
}
doublepower(double dDishu,int iMi)
{
int iCount;
doubledResult = 1.0;
for(iCount=1;iCount<=iMi;iCount++)
dResult = dResult*dDishu;
return(dResult);
}
第 2章 函数输入符合要求时 ( 仅有字符 0和 1) 的程序运行结果为输入一个 8位的二进制数,11010011
十进制值,211
输入不符合要求时 ( 含有除 0和 1以外的任何字符 )
的程序运行结果为输入一个 8位的二进制数,110lao11
这不是一个二进制数 ! 不能正确转换第 2章 函数
【 例 2-4】 编写一个函数可用来判断任给的一个正整数是否为素数 (或质数 )。 再编写主程序完成输入,调用和输出 。 素数是指只能被 1和它自身整除的数 。
分析:素数的逆定义就是,一但某数 n能被 2、
3,...,n-1中的任何一个数除尽 (只要除法中有一次余数为零 ),则 n肯定不是一个素数;某数 n若依次除以 2、
3,...,n-1,结果都除不尽 (有余数 ),则 n肯定是一个素数 。
第 2章 函数程序代码如下:
#include <iostream.h>
int iIsprime(int iNum);
void main( )
{
int iNum;
cout<<"请输入一个正整数,";
cin>>iNum;
if(iIsprime(iNum)==1)
cout<<iNum<<" 是一个素数,"<<endl;
第 2章 函数
else
cout<<iNum<<" 不是一个素数,"<<endl;
}
int iIsprime(int iNum)
{
int iChushu;
bool bFlag=false;
for(iChushu=2;iChushu<=iNum-1;iChushu++)
{
if(iNum%iChushu==0)
{
bFlag=true;
第 2章 函数
break;
}
}
if(bFlag==false)
return 1;
else
return 0;
}
第 2章 函数第一次程序运行结果为请输入一个正整数,31
31是一个素数第二次程序运行结果为请输入一个正整数,119
119不是一个素数第 2章 函数
2.1.3 函数的参数传递函数的参数用于在调用函数与被调用函数之间进行数据传递 。 在函数定义时,函数名后面括号内的参数称为形式参数 (简称形参 )。 在函数被调用时,函数名后面括号内的参数称为实际参数 (简称实参 )。
第 2章 函数当函数未被调用时,C++编译系统并没有给函数的形参分配相应的内存空间,函数的形参更不会有实际的值 。 只有在函数被调用时,C++编译系统这时才为形参分配实际的存储单元,并将实参与形参结合 。 实参可以是常量,变量或表达式,其类型必须与形参相符 。 函数的参数传递,指的就是形参与实参结合 (简称形实结合 )的过程 。 形实结合的方式有值调用和引用调用两种 。
第 2章 函数
1,值调用值调用是指当发生函数调用时,编译系统为形参分配相应的存储空间并且直接将实参的值复制给形参,
这样形参和实参就各自拥有不同的存储单元,且形参是实参的副本 。 因此,值调用过程是参数值的单向传递过程,一旦形参获得了与实参相同的值就与实参脱离关系,以后不论形参发生多大的改变,都决不会反过来影响到实参 。 前面 2.1.2节中的四道例题均属于值调用方式 。
第 2章 函数
【 例 2-5】 从键盘输入两个整数,交换位置后输出 ( 交换未成功 ) 。
#include <iostream.h>
void swap(int a,int b);
void main( )
{
int x,y;
x=5;
y=10;
cout<<"x="<<x<<" y="<<y<<endl;
swap(x,y); //交换 x,y的值第 2章 函数
cout<<"after swap"<<endl;
cout<<"x="<<x<<" y="<<y<<endl;
}
void swap(int a,int b)
{
int t;
t=a;
a=b;
b=t;
}
第 2章 函数程序运行结果为
x=5 y=10
after swap
x=5 y=10
分析:从上面的程序运行结果可以看出,并没有达到交换的目的 。 这是因为采用的传递方式不合乎问题的要求 。 在单向值传递方式中,形参值虽确实进行了交换,但这些改变对实参不起任何作用 。
第 2章 函数执行主调函数中的函数调用语句 swap(x,y)后,编译系统将实参 x中的值 5传递给虚参 a,将实参 y中的值
10传递给虚参 b;在 swap函数中,a,b中的值完成互换;
返回主函数时,实参 x,y中的值不受虚参 a,b的影响,
并未进行交换 。
第 2章 函数
2,引用调用显而易见,值调用时参数的传递方式是实参单向复制其值给虚参,如果我们想使子函数中对形参所做的任何更改也能及时反映给主函数中的实参 ( 即希望形参与实参的影响是互相的或称是双向的 ),又该怎么办呢? 这就需要改变调用方式,即采用第二种参数传递方式 ——引用调用 。
引用是一种特殊类型的变量,可以被认为是某一个变量的别名。通过引用名与通过被引用的变量名访问变量的效果是一样的。这就是说,对形参的任何操作也就直接作用于实参。
第 2章 函数例如:
int a,b;
int &ra=a; //建立一个 int型的引用 ra,并将其初始化为变量 a的一个别名
b=10;
ri=b; //相当于 a=b;
注意:
① 声明一个引用时,必须同时对它进行初始化,
使它与一个已存在的对象关联 。
第 2章 函数
② 一旦一个引用被初始化后,就不能改变关联对象 。 换言之,一个引用从它被声明之后,就必须确定是哪个变量的别名,而且自始至终只能作为这一个变量的别名,不能另作他用 。
形参也可以引用的方式出现在形参表中。引用作为形参的情况与变量的引用稍有不同。这是因为,形参的初始化不在类型说明时进行,而是在执行主调函数中的调用语句时,才为形参分配内存空间,同时用实参来初始化形参。
第 2章 函数
【 例 2-6】 使用引用调用改写例 2-5的程序,使两实参中的数真正进行互换 。
#include <iostream.h>
void swap(int &a,int &b);
void main( )
{
int x,y;
x=5;
y=10;
cout<<"x="<<x<<" y="<<y<<endl;
swap(x,y); //交换 x,y的值
cout<<"after swap"<<endl;
cout<<"x="<<x<<" y="<<y<<endl;
第 2章 函数
}
void swap(int &a,int &b)
{
int t;
t=a;
a=b;
b=t;
}
程序运行结果为
x=5 y=10
after swap
x=10 y=5
第 2章 函数分析:子函数 swap的两个参数都是引用,当被调用时,它们分别被初始化成为 a和 b的别名 。 因此,在子函数 swap中将两个参数的值进行交换后,交换结果可以返回主函数 main。
第 2章 函数
2.2 函数调用机制一个 C++的源程序经过编译以后形成与源程序主名相同但后缀为,exe的可执行文件,且存放在外存储器中。
当该,exe的可执行程序被运行时,首先从外存将程序代码装载到内存的代码区,然后从 main函数的起始处开始执行。程序在执行过程中,如果遇到了对其它函数的调用,则暂停当前函数的执行,
第 2章 函数保存下一条指令的地址 (即返回地址,作为从子函数返回后继续执行的入口点 ),并保存现场 (主要是一些寄存器的内容 ),然后转到子函数的入口地址,执行子函数 。 当遇到 return语句或者子函数结束时,则恢复先前保存的现场,并从先前保存的返回地址开始继续执行 。 图 2-1说明了函数调用和返回的过程,图中标号标明了执行顺序 。
第 2章 函数图 2-1 函数调用和返回的示意图第 2章 函数
【 例 2-7】 求设 N=10,X=2,4,6,8,即求 N事件中每次取 2、
4,6,8的组合数 。
分析:这个问题需要反复利用两个公式:
① N!
② N!/X!/(N-X)!
设计两个函数:一个求整数阶乘的函数 lJiecheng和一个求组合数的函数 lComb。 由主函数 main调用 lComb,
lComb又调用 lJiecheng。
!
! ( ) !
X
N
NC
X N X
第 2章 函数程序代码如下:
#include <iostream.h>
long lJiecheng(int n)
{
long rt=1;
int i;
for(i=1;i<=n;i++)
rt=rt*i;
return rt;
}
long lComb(int N,int X)
第 2章 函数
{
return lJiecheng(N)/lJiecheng(X)/lJiecheng(N-X);
}
void main( )
{
long lJiecheng(int n);
long lComb(int N,int X);
int iNum,x;
do
{
第 2章 函数
cout<<"请输入事件数 ( 大于等于 8),";
cin>>iNum;
}while(iNum<10);
for(x=2;x<10;x+=2)
cout<<"C("<<iNum<<","<<x<<")="<<lComb(iNum,x)<<endl;
}
程序运行结果为请输入事件数 (大于等于 8),11
C(11,2)=55
C(11,4)=330
C(11,6)=462
C(11,8)=165
第 2章 函数
2.3 递 归 函 数递归函数又称为自调用函数,其特点是,在函数内部直接或间接地自己调用自己 。 所谓直接调用自身,就是指在一个函数的函数体中出现了对自身的调用语句 。
例如:
void func1(void)
{
...
func1( ); //func1调用 func1自身
...
}
第 2章 函数所谓间接调用自身,就是一个函数 func1调用另一个函数 func2,而函数 func2中又调用了函数 func1,于是构成间接递归 。 下面的例子属于间接调用情况 。
void func1(void)
{
...
func2( ); //func1调用 func2
...
}
void func2(void)
{
第 2章 函数
...
func1( ); //func2调用 func1
...
}
func1函数就是通过 func2实现间接递归 。
递归算法的实质是将原有的问题分解为新的问题,
而解决新问题时又用到了原有问题的解法。按照这一原则分解下去,每次出现的新问题都是原有问题的简化的子集,而最终分解出来的问题,是一个已知解的问题。这便是有限的递归调用。
第 2章 函数
【 例 2-8】 编写函数,用递归的方法求 n! 的值 。 在主程序中实现任意输入 n值并输出计算结果 。
分析:计算 n!的公式如下 。
1(n=0)
n!=
n(n-1)! (n>0)
这是一个递归形式的公式,在描述阶乘算法时,
又用到了阶乘,只不过求阶乘的数在逐次减 1,因而编程时也自然采用递归算法 。 递归的结束条件是 n=0。
第 2章 函数程序代码如下:
#include <iostream.h>
long jc(int n)
{
long rt;
if(n<0)
cout<<"Data error!"<<endl;
else if(n==0) rt=1; //递归的结束条件
else rt=n*jc(n-1); //以参数减 1的方式继续递归
return rt;
第 2章 函数
}
void main( )
{
long jc(int n);
int n;
long result;
do
{
cout<<"输入一个正整数,";
cin>>n;
}while(n<=0);
第 2章 函数
result=jc(n); //首次调用处
cout<<n<<"!="<<result<<endl;
}
程序运行结果为输入一个正整数,6
6!=720
第 2章 函数
【 例 2-9】 有 5个人坐在一起,问第 1个人多少岁,
他说比第 2个人大 2岁 。 问第 2个人多少岁,他说比第 3
个人大 2岁 。 问第 3个人多少岁,他说比第 4个人大 2岁 。
问第 4个人多少岁,他说比第 5个人大 2岁 。 最后问第 5
个人,他说是 12岁 。 请问第 1个人多少岁?
分析:这是一个递归问题 。 每一个人的年龄都比其后那个人的年龄大 2,即第 2章 函数
age(1)=age(2)+2
age(2)=age(3)+2
age(3)=age(4)+2
age(4)=age(5)+2
age(5)=12
可以用公式表示如下:
12(n=5)
age(n)=
age(n+1)+2 (n<5)
第 2章 函数程序代码如下:
#include <iostream.h>
int age(int n)
{
int ss;
if(n==5)ss=12;
elsess=age(n+1)+2;
return(ss);
}
void main( )
第 2章 函数
{
int age(int n);
cout<<"第一个人的年龄为 "<<age(1)<<"岁 "<<endl;
}
程序运行结果为第一个人的年龄为 20岁第 2章 函数
2.4 默认参数的函数在函数定义中通过赋值运算就可指定默认参数值。
一旦程序在调用该函数时,如果给出实参,则用实参初始化形参;如果没有给出实参,则 C++编译系统自动以预先赋值的默认参数值作为传入数值。一般情况下都将调用该函数时经常用到的常数作为默认参数值,
这样在调用时就无需每次都写出该值了。指定默认参数值可以使函数的使用更为简单,同时也增强了函数的可重用性。
第 2章 函数
【 例 2-10】 带默认形参值的函数例题 。
#include <iostream.h>
int mult(int n,int k=2) //第二个形参具有默认值
{
if(k==2)
return (n*n);
else
return (mult(n,k-1)*n);
}
void main( )
第 2章 函数
{
cout<<endl<<mult(5)<<endl;
/* 形参 n用实参来初始化为 5,形参 k采用默认值 2,实现 5*5*/
cout<<mult(5,3)<<endl;
/* 用实参来初始化形参,n为 5,k为 3,实现 5*5*5*/
}
程序运行结果为
25
125
第 2章 函数默认形参值必须按从右向左的顺序定义 。 在有默认值的形参右面,不能出现无默认值的形参 。 因为在调用时,实参初始化形参是按从左向右的顺序 。 例如:
void try(int j=3,int k) //非法
void try(int j,int k=2,int m) //非法
void try(int j,int k=7) //合法
void try(int j,int k=2,int m=3) //合法
void try(int j=3,int k=2,int m=3) //合法第 2章 函数默认形参值应该在函数原型中给出 。 例如:
int multi(int x=2,int y=5); //默认形参值在函数原型中给出
void main( )
{
multi( ); //并非无参调用,而是采用默认值,x取值 2,y取值 5
}
int multi(int x,int y)
{
return(x*y);
}
第 2章 函数在相同的作用域内,默认形参值的说明应保持唯一 。 但如果在不同的作用域内,允许说明不同的默认形参 。 这里的作用域是指直接包含着函数原型说明的大括号所界定的范围 。 对作用域概念的详细介绍请参阅第 5章 。 例如:
int add(int x=2,int y=5); //全局默认形参值
void main( )
{
int add(int x=1,int y=9); //局部默认形参值第 2章 函数
add( ); //此处调用时,采用局部默认形参值,x取值 1,y
取值 9
}
void func(void)
{
add( ) //此处调用时,采用全局默认形参值,x取值 2,y
取值 5
}
第 2章 函数
2.5 内 联 函 数内联函数 (也称在线函数 )是在 C++中为提高程序运行效率而引入的。所有函数调用时都会产生一些额外的开销,主要是系统栈的保护、代码的传递、系统栈的恢复以及参数传递等。对于一些函数体很小但又经常使用的函数,由于被调用的频率非常高,这种额外开销也就很可观,有时甚至会对运行效率产生本质的影响。
第 2章 函数使用内联函数正是解决这一问题的手段 。
内联函数不是在调用时发生转移,而是在编译时将函数体嵌入在每一个调用语句处 。 这样就相对节省了参数传递,系统栈的保护与恢复等的开销 。
内联函数在定义时使用关键字 inline区别于一般函数,其语法形式如下:
<inline><类型标识符 ><被调函数名 > (含类型说明的形参表 )
{
函数体
}
第 2章 函数例如:
inline int mul(int a,int b)
{
return a*b;
}
当程序中出现 mul(2+3,4)的函数调用时,编译程序就会将其扩展为 (2+3)*4。 关键字 inline是一个编译命令,编译程序在遇到这个命令时将记录下来,在处理内联函数的调用时,编译程序就试图产生扩展码 。 这样从使用者的角度来看,内联函数在语法上与一般函数没有什么区别,只是在编译程序生成目标代码时才区别处理 。
第 2章 函数注意:
① 内联函数体内一般不能有循环语句和 switch语句 。
② 内联函数的定义必须出现在第一次被调用之前 。
③ 对内联函数不能进行异常接口声明 。
如果违背了上述注意事项中的任一项,编译程序就会无视关键字 inline的存在,像处理一般函数一样处理,不生成扩展代码。因此,只有很简单而使用频率很高的函数才被说明为内联函数。内联函数会扩大目标代码,使用时要谨慎。
第 2章 函数
【 例 2-11】 内联函数例题 。
#include <iostream.h>
#include <iomanip.h>
inline int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
第 2章 函数
void main( )
{
int a,b,c,d;
a=210;
b=150;
c=20;
d=max(a,b);
d=max(d,c);
//编译时两个调用处均被替换为 max函数体语句 。
cout<<"The biggest of"
<<setw(5)<<a
<<setw(5)<<b
第 2章 函数
<<setw(5)<<c<<" is "<<d<<endl;
}
程序运行结果为
The biggest of21015020is210
第 2章 函数
2.6 函 数 重 载函数的重载也称多态函数 。 C++编译系统允许为两个或两个以上的函数取相同的函数名,但是形参的个数或者形参的类型不应相同,编译系统会根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数,这就是所谓的函数重载 。
第 2章 函数对于没有重载机制的 C语言,每个函数必须有其不同于其它函数的名称,即使操作是相同的,仅仅数据的类型不相同,也需要定义名称完全不同的函数,
这样就显得重复且效率低下 。 例如,定义求平方函数,
就必须对整数的平方,浮点数的平方以及双精度数的平方分别用不同的函数名:
intisq(int x,int y);
float fsq(float x,float y);
double dsq(double x,double y);
第 2章 函数程序在调用这三个不同类型的函数时,是以名字加以区别的,需要记住并区别它们的名称 。 显然,这样就造成了代码的重复,使用起来也不方便,更不利于代码的维护 。
对于具有重载机制的 C++语言,允许功能相近的函数在相同的作用域内以相同函数名定义,因而使函数方便使用,便于记忆,也使程序设计更加灵活 。 仍以上例而言,在 C++中只要用一个函数名即可,如
square( ),然后以赋给此函数的参数类型来决定是要计算 int型,float型,还是 double型的数的平方 。 上例在
C++中的定义形式如下:
第 2章 函数
int square(int x);
float square(float x);
double square(double x);
要计算 square(3)时,C++自动使用第一种形式;计算 square(3.25)时,C++自动使用第三种形式;计算
square(3.25f)时,C++自动使用第二种形式 。
但是决不可以定义两个具有相同名称,相同参数类型和相同参数个数,只是函数返回值不同的重载函数 。
第 2章 函数例如,以下定义是 C++不允许的:
int func(int x);
float func(int x);
由此可见,C++是按函数的参数表分辨相同名称的函数 。 如果参数表相同,则认为是错误的说明 。
C++允许重载函数有数量不同的参数个数 。 当函数名相同而参数个数不同时,C++会自动按参数个数定向到正确的要调用的函数 。 下例说明了 C++的这一特性 。
第 2章 函数
【 例 2-12】 重载函数应用例题 。
#include <iostream.h>
int add(int x,int y)
{
int sum;
sum=x+y;
return(sum);
}
int add(int x,int y,int z)
{
第 2章 函数
int sum;
sum=x+y+z;
return(sum);
}
void main( )
{
int a,b;
a=add(5,10);
b=add(5,10,20);
cout<<"a="<<a<<"b="<<b<<endl;
}
程序运行结果为
a=15b=35
第 2章 函数
2.7 函 数 模 板有很多时候,我们希望所设计的算法可以处理多种数据类型 。 但是,即使这一算法被设计为重载函数,
也只是使用相同的函数名,函数体仍然要分别定义 。
如下面两个求较小值的函数:
int small(int x,int y)
{
return x<y?x:y;
}
第 2章 函数
double small(double x,double y)
{
return x<y?x:y;
}
考察以上两个函数,有如下特点:只有参数类型不同,返回值类型不同,功能则完全一样。类似这样的情况,可以使用函数模板,从而避免函数体的重复定义。
第 2章 函数函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,简化重载函数的函数体设计 。 它的最大特点是把函数所使用的数据类型作为参数 。
函数模板的定义形式如下:
<template> <typename标识符 >
函数定义第 2章 函数
【 例 2-13】 定义一个能交换两个变量值的函数,要求用模板函数实现 。
#include <iostream.h>
template <typename T>
void swap(T &x,T &y)
{
T z;
z=y;y=x;x=z;
}
void main( )
第 2章 函数
{
int m=1,n=8;
double u= -5.5,v=99.3;
cout<<"m="<<m<<" n="<<n<<endl;
cout<<"u="<<u<<" v="<<v<<endl;
swap(m,n); //整型
swap(u,v); //双精度型
cout<<"m与 n,u与 v交换以后,"<<endl;
cout<<"m="<<m<<" n="<<n<<endl;
cout<<"u="<<u<<" v="<<v<<endl;
}
第 2章 函数程序运行结果为
m=1 n=8
u= -5.5 v=99.3
m与 n,u与 v交换以后:
m=8 n=1
u=99.3 v= -5.5
分析:编译系统从调用 swap( )时,根据实参的类型推导出函数模板的类型参数 。
第 2章 函数于调用 swap(m,n),由于实参 m及 n为 int类型,所以,推导出模板中类型参数 T为 int。
当类型参数的含义确定后,编译器将以函数模板为样板生成一个函数:
int swap(int &x,int &y)
{
int z;
z=y; y=x; x=z;
}
同样,对于调用 swap(u,v),由于实参 u,v为
double类型,所以,推导出模板中类型参数 T为 double。
第 2章 函数接着,编译器将以函数模板为样板,生成如下函数:
double swap(double &x,double &y)
{
double z;
z=y; y=x; x=z;
}
因此,当主函数第一次调用 swap( )时,执行的实际上是由函数模板生成的函数,
int swap( int &x,int &y)
当主函数第二次调用 swap( )时,执行的实际上是由函数模板生成的函数,
double swap( double &x,double &y)
第 2章 函数
2.8 使用 C++系统函数
C++不仅允许我们根据需要自定义函数,而且 C++
的系统库中还提供了几百个常用函数可供程序员使用 。
如求平方根函数 (sqrt),求浮点数或双精度数的绝对值函数 (fabs),对数函数 (log),指数函数 (exp),三角函数等都属于数学函数 。 输入 /输出格式控制函数有 setw( )、
setprecision( )等 。
第 2章 函数由前面已学习的知识可知,调用函数之前必须先声明函数原型。系统函数的原型声明已经全部由系统提供了,并且已分类存在于不同的头文件中。程序员需要做的事情,就是用 include指令嵌入相应的头文件,
然后便可以使用系统函数了。例如,要使用基本输入 /
输出流函数 cin( )/cout( )函数,就必须嵌入头文件
iostream.h。
第 2章 函数若要使用数学函数,如求绝对值函数 abs( ),fabs( ),
三角函数 sin( ),cos( ),tan( ),开平方函数 sqrt( ),对数值函数 log( ),以 e为底的指数函数 exp( ),就要嵌入头文件 math.h。同样,要使用输入 /输出格式控制函数
setw( )/setprecision( ),就要嵌入头文件 iomanip.h。
第 2章 函数
【 例 2-14】 系统函数应用例题 。
从键盘输入一个角度值,求出该角度的正弦值,
余弦值和正切值 。
分析:系统函数中提供了求正弦值,余弦值和正切值的函数 —— sin( ),cos( )及 tan( )函数的说明在头文件
math.h中 。 同样,系统函数中也提供了输入 /输出格式控制函数,如输出域宽控制函数 setw( ),输出精度控制函数 setprecision( ),函数的说明在头文件 iomanip.h中 。
因此,需要用到这些系统函数时,就必须将该函数所属的头文件以 #include <头文件名 >或 #include“头文件名,的形式写在程序代码开始部分 。
第 2章 函数程序代码如下:
#include <iostream.h>
#include <math.h>
#include <iomanip.h>
const double pi=3.14159265;
void main( )
{
double a,b;
cin>>a;
b=a*pi/180;
第 2章 函数
cout<<"sin("<<a<<")="<<setw(10)<<sin(b)<<endl;
cout<<"cos("<<a<<")="<<setw(10)<<cos(b)<<endl;
cout<<"tan("<<a<<")="<<setw(10)<<tan(b)<<endl;
}
程序运行结果为输入,30
sin(30)= 0.5
cos(30)=0.866025
tan(30)= 0.57735
第 2章 函数充分利用系统函数,可以大大减少编程的工作量,
提高程序的运行效率和可靠性 。 要使用系统函数,应该注意以下两点:
① 了解所使用的 C++开发环境提供了哪些系统函数。不同的编译系统提供的系统函数有所不同。
② 确定要使用的系统函数的声明在哪个头文件中。
这也可以在库函数参考手册或联机帮助中查到。
第 2章 函数例如,在 MSDN Library Visual Studio 6.0中查找
VC++6.0系统函数的分类列表:首先在“活动子集”栏选择 Visual C++ Documentation,然后按如下路径选择:
Visual C++ Documentation → Using Visual C++ →
Visual C++ Programmer's Guide → Run -Time Library
Reference → Run -Time Routines by Category → Run -
Time Routines byCategory,如图 2-2。该帮助系统中将函数按如下分类列出:
第 2章 函数获取参数 ( Argument access)
浮点支持 ( Floating-point support)
缓冲区操作 ( Buffer manipulation)
输入与输出 ( Input and output)
字节分类 ( Byte classification)
国际化 ( Internationalization)
字符分类 ( Character classification)
内存分配 ( Memory allocation)
数据转换 (Data conversion)
第 2章 函数处理机与环境控制 ( Process and environment control)
调试 ( Debug)
查找与排序 ( Searching and sorting)
目录控制 ( Directory control)
字符串操作 ( String manipulation)
错误处理 ( Error handling)
系统调用 ( System calls)
异常处理 ( Exception handling)
时间管理 ( Time management)
文件处理 ( File handling)
第 2章 函数图 2-2 MSDN Library Visual Studio 6.0窗口