C++语言程序设计 第三章 函数
本章主要内容 函数的声明和调用 函数间的参数传递 引用 内联函数 带缺省形参值的函数 函数重载 函数模板 C++系统函数
函数的定义 函数的声明与使用 函数是面向对象程序设计中的基本抽象单元,是对功能的抽象 函数定义的语法形式 类型标识符 函数名(形式参数表) 类型标识符 函数名(形式参数表) { 语句序列 } 若无参数,写void 是被初始化的内部变量,寿命和可见性仅限于函数内部 若无返回值,写void
函数的定义 函数的声明与使用 形式参数表 <type1> name1, <type2> name2, ..., <typen> namen 函数的返回值 由 return 语句给出,例如: return 0 无返回值的函数(void类型),不必写return语句。
函数的调用 函数的声明与使用 调用前先声明函数原型: 在调用函数的说明部分,或程序文件开头所有函数之前,按如下形式说明: 类型标识符 被调用函数名 (含类型说明的形参表); 调用形式 函数名(实参列表) 实参列表中应给出与函数原型形参个数相同、类型相符的实参。 嵌套调用 函数不允许嵌套定义(定义一个函数时,函数体内又包含另一个函数的完整定义,叫嵌套定义)。 可以嵌套调用。 递归调用 函数直接或间接调用自身。
例3-1 编写一个求x的n次方的函数 函数的声明与使用 #include <iostream.h> double power (double x, int n); void main(void) { cout << "5 to the power 2 is " << power(5,2) << endl; } double power (double x, int n) double val = 1.0; while (n--) val = val*x; return(val);
例3-1 编写一个求x的n次方的函数 函数的声明与使用 运行结果: 5 to the power 2 is 25
例3-3 编写程序求π的值 函数的声明与使用 其中arctan用如下形式的级数计算: 直到级数某项绝对值不大于10-15为止;π和x均为double型。
#include<iostream.h> void main( ) { double a,b; double arctan(double x) ; a=16.0*arctan(1/5.0) ; b=4.0*arctan(1/239.0) ; //注意:因为整数相除结果取整, //如果参数写1/5,1/239,结果就都是0 cout<<"PI="<<a-b<<endl; }
运行结果: PI=3.14159 double arctan(double x) { int i; double r,e,f,sqr; sqr=x*x; r=0; e=x; i=1; while(e/i>1e-15) { f=e/i; r=(i%4==1)? r+f : r-f ; //i=1,5,9,13,…… e=e*sqr; i+=2; } return r ; 运行结果: PI=3.14159
运行结果: PI=3.14159
例3-4 函数的声明与使用 寻找并输出11~999之间的数m,它满足m、m2和m3均为回文数。 分析: 回文:各位数字左右对称的整数。 例如:11满足上述条件 112=121,113=1331。 分析: 10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。
#include <iostream.h> void main( ) { bool symm(long n); long m; for(m=11; m<1000; m++) if (symm(m)&&symm(m*m)&&symm(m*m*m)) cout<<"m="<<m<<" m*m="<<m*m<<" m*m*m="<<m*m*m<<endl; }
bool symm(long n) { long i, m; i=n ; m=0 ; while(i) m=m*10+i%10; i=i/10 ; } return ( m==n );
运行结果: m=11 m*m=121 m*m*m=1331 m=101 m*m=10201 m*m*m=1030301 m=111 m*m=12321 m*m*m=1367631
函数调用的执行过程 函数的声明与使用 main( ) 调fun( ) 结束 ① ② ④ ⑥ ⑦ ③ ⑤ 保存: 返回地址 fun( ) 当前现场 ③ 恢复: 主调程序现场 ⑤
嵌套调用 函数的声明与使用 main{} 调fun1( ) 结束 fun1( ) 调fun2( ) 返回 fun2( ) 返回 ① ② ③ ④ ⑤ ⑨ ⑦ ⑧ ⑥
例3-7 输入两个整数,求平方和。 函数的声明与使用 #include <iostream.h> void main(void) { int a,b; int fun1(int x,int y); cin>>a>>b; cout<<"a、b的平方和:" <<fun1(a,b)<<endl; }
int fun1(int x,int y) { int fun2(int m); return (fun2(x)+fun2(y)); } int fun2(int m) return (m*m); 运行结果: 3 4 a、b的平方和:25
递归调用 函数的声明与使用 函数直接或间接地调用自身,称为递归调用。 递归过程的两个阶段: 递推: 未知 已知 回归: 4!=4×3! → 3!=3×2! → 2!=2×1! → 1!=1×0! → 0!=1 未知 已知 回归: 4!=4×3!=24←3!=3×2!=6←2!=2×1!=2←1!=1×0!=1←0!=1 未知 已知
例3-8 求n! 函数的声明与使用 分析:计算n!的公式如下: 这是一个递归形式的公式,应该用递归函数实现。
源程序: #include <iostream.h> long fac(int n) { long f; if (n<0) cout<<"n<0,data error!"<<endl; else if (n==0) f=1; else f=fac(n-1)*n; return(f); }
void main( ) { long fac(int n); int n; long y; cout<<"Enter a positive integer:"; cin>>n; y=fac(n); cout<<n<<"!="<<y<<endl; } 运行结果: Enter a positive integer:8 8!=40320
函数的参数传递机制 ——传递参数值 函数的声明与使用 在函数被调用时才分配形参的存储单元。 实参可以是常量、变量或表达式。 函数的参数传递机制 ——传递参数值 函数的声明与使用 在函数被调用时才分配形参的存储单元。 实参可以是常量、变量或表达式。 实参类型必须与形参相符。 传递时是传递参数值,即单向传递。
函数的参数传递机制 ——参数值传递举例 函数的声明与使用 X N 被调函数: 主调函数: 3 2.5 A D = power(A,3) 函数的参数传递机制 ——参数值传递举例 函数的声明与使用 X N 被调函数: 主调函数: 3 2.5 A D = power(A,3) double power(double X, int N)
例3-11 输入两 整数交换后输出 函数的声明与使用 #include<iostream.h> void Swap(int a, int b); int main( ) { int x(5), y(10); cout<<"x="<<x<<" y="<<y<<endl; Swap(x,y); return 0; } 交换函数 Swap 采用值传递,未达到交换效果。
void Swap(int a, int b) { int t; t=a; a=b; b=t; } 运行结果: x=5 y=10
函数的参数传递 ——用引用做形参 函数的声明与使用 引用(&)是标识符的别名,例如: 函数的参数传递 ——用引用做形参 函数的声明与使用 引用(&)是标识符的别名,例如: int i,j; int &ri=i; //建立一个int型的引用ri,并将其 //初始化为变量i的一个别名 j=10; ri=j;//相当于 i=j; 声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。 一旦一个引用被初始化后,就不能改为指向其它对象。 引用可以作为形参 void swap(int& a, int& b) {...}
例3-12 输入两个整数交换后输出 函数的声明与使用 #include<iostream.h> void Swap(int& a,int& b); int main( ) { int x(5), y(10); cout<<"x="<<x<<" y="<<y<<endl; Swap(x,y); //Swap(&x,&y); return 0; } 交换函数以引用为参数,达到了交换目的。
void Swap(int& a, int& b) int t; t=a; a=b; b=t; } 运行结果: x=5 y=10 x=10 y=5
例3-13 引用调用举例 函数的声明与使用 #include <iostream.h> #include <iomanip.h> void fiddle(int in1, int &in2); int main( ) { int count = 7, index = 12; cout << "The values are "; cout<<setw(5)<<count; cout<<setw(5)<<index<<endl; fiddle(count, index); return 0; }
void fiddle(int in1, int &in2) { in1 = in1 + 100; in2 = in2 + 100; cout << "The values are "; cout<<setw(5)<<in1; cout<<setw(5)<<in2<<endl; } 运行结果: The values are 7 12 The values are 107 112 The values are 7 112 void fiddle(int in1, int &in2) 第二个参数是引用,因此在子函数中改变了主函数的变量 index 。
引用 引用不是值,不占存储空间,声明引用时,目标的存储状态不会改变。 引用运算符与地址操作符使用相同的符号。但它们是不一样的。 引用运算符只在声明的时候使用,它放在类型名后面。例: int& rint=intone; 任何其他“&”的使用都是地址操作符。 例 int *ip=&intone; cout<<&ip; 引用一旦初始化,它就维系在一定的目标上,再也不分开。任何对该引用的赋值都是对引用所维系的目标赋值,而不是将引用维系到另一个目标上。见例子引用.cpp
#include<iostream.h> void main() { int intOne; int & rInt=intOne; intOne=5; cout<<"intOne:"<<intOne<<endl; cout<<"rInt:"<<rInt<<endl; cout<<"&intOne:"<<&intOne<<endl; cout<<"&rInt:"<<&rInt<<endl;
int intTwo=8; rInt=intTwo; cout<<"intOne:"<<intOne<<endl; cout<<"intTwo:"<<intTwo<<endl; cout<<"rInt:"<<rInt<<endl; cout<<"&intOne:"<<&intOne<<endl; cout<<"&intTwo:"<<&intTwo<<endl; cout<<"&rInt:"<<&rInt<<endl; }
运行结果: intOne:5 rInt:5 &intOne:0110F150 &rInt:0110F150 intOne:8 intTwo:8 rInt:8 &intOne:0110F150 &intTwo:0110F14E &rInt:0110F150 intOne:5 rInt:5 &intOne:0110F150 &rInt:0110F150
引用 引用调用:用引用作为形参的函数调用。 将引用作为形参,用实参来初始化形参,成为实参的一个别名,对形参的任何操作也就会直接作用于实参。 引用与指针有很大差别: 指针是个变量,可以把它再赋值成指向别处的地址。 建立引用时必须进行初始化,决不会再将其关联到其它不同的变量。 引用调用:用引用作为形参的函数调用。 将引用作为形参,用实参来初始化形参,成为实参的一个别名,对形参的任何操作也就会直接作用于实参。 引用传递的内存布局与指针相仿,只是操作完全不同。
内联函数的需要性 内联函数(内嵌函数)解决程序的运行效率。 函数调用需要建立栈内存环境,进行参数传递,并产生程序执行转移,这些工作都需要一些时间开销。 有些函数使用频率高,但代码却很短,将其声明为内联函数。即在函数声明和定义中:inline int isnumer(char c); inline int isnumber(char c) { return (ch>=‘0’&&ch<=‘9’)?1:0; }
内联函数声明与使用 内联函数 声明时使用关键字 inline。 编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销。 注意: 内联函数体内不能有循环语句和switch语句。递归函数不能被用来做内联函数。 内联函数不能含有任何静态数据及数组声明。 内联函数的声明必须出现在内联函数第一次被调用之前。 内联函数与调用函数或内联成员函数与类定义部分必须在同一个文件中,不能放在两个文件中。 内联函数
例3-14 内联函数应用举例 内联函数 #include<iostream.h> inline double CalArea(double radius) { return 3.14*radius*radius; } int main( ) double r(3.0); double area; area=CalArea(r); cout<<area<<endl; return 0; 内联函数
内联函数 编译器看到inline后,为该函数创建一段代码,以便在后面每次碰到该函数的调用都用相应的一段代码来替换。 内联函数与宏定义 在C中,常用预处理语句#define来代替一个函数的定义。 例: #define MAX(a,b) ((a)>(b)?(a):(b)) 宏定义语句书写格式过分讲究,不会对函数参数类型进行检查。 内联函数可以得到所有宏替换效能和常规函数类型检查。
缺省形参值的作用 带缺省形参值的函数 函数在声明时可以预先给出默认的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的默认形参值。 例如: void main(void) { add(10,20); //10+20 add(10); //10+6 add( ); //5+6 } int add(int x=5,int y=6) { return x+y; }
缺省形参值的说明次序 带缺省形参值的函数 缺省形参值必须从右向左顺序声明,并且在缺省形参值的右面不能有非缺省形参值的参数。因为调用时实参取代形参是从左向右的顺序。 例: int add(int x,int y=5,int z=6); //正确 int add(int x=1,int y=5,int z); //错误 int add(int x=1,int y,int z=6); //错误 函数调用: add(10,8,11); add(9);
缺省形参值与函数的调用位置 带缺省形参值的函数 调用出现在函数体实现之前时,缺省形参值必须在函数原形中给出;而当调用出现在函数体实现之后时,缺省形参值需在函数实现时给出。 例: int add(int x=5,int y=6); void main(void) { add( ); //调用在实现前 } int add(int x,int y) { return x+y; } int add(int x=5,int y=6) { return x+y; } void main(void) { add( ); //调用在实现后 }
缺省形参值的作用域 带缺省形参值的函数 在相同的作用域内,缺省形参值的说明应保持唯一,但如果在不同的作用域内,允许说明不同的缺省形参。 例: int add(int x=1,int y=2); void main(void) { int add(int x=3,int y=4); add( ); //使用局部缺省形参值(实现3+4) } void fun(void) { ... add( ); //使用全局缺省形参值(实现1+2)
例3-15 带缺省形参值的函数举例 带缺省形参值的函数 #include <iostream.h> #include <iomanip.h> int get_volume(int length, int width = 2, int height = 3); int main( ) {int x = 10, y = 12, z = 15; cout << "Some box data is " ; cout << get_volume(x, y, z) << endl; cout << get_volume(x, y) << endl; cout << get_volume(x) << endl; cout << "Some box data is "; cout << get_volume(x, 7) << endl; cout << get_volume(5, 5, 5) << endl; return 0; } ①题意:给出长、宽、高,get_volum 函数计算出体积。 使用缺省形参值。 ②先执行子函数中的输出,再执行主函数中的输出。
运行结果: Some box data is 10 12 15 1800 Some box data is 10 12 3 360 int get_volume(int length, int width, int height) { cout<<setw(5)<<length <<setw(5)<<width<<setw(5)<<height<<' '; return length * width * height; } 运行结果: Some box data is 10 12 15 1800 Some box data is 10 12 3 360 Some box data is 10 2 3 60 Some box data is 10 7 3 210 Some box data is 5 5 5 125
函数重载的需要性 在C中,要完成两个变量相加,根据变量类型不同,规定不同的函数名。 int iadd(int,int); long ladd(long,long); double dadd(double,double);char cadd(char,char); 用C处理,需给四个函数规定四个不同的函数名,程序员需记住四件事。 在C++中,引入重载,只需给这四个函数起一个共同的名字add,它们的参数类型不同,当调用函数时,编译器根据实参类型来确定到底调用哪个重载函数,程序员只需记住add一件事,其余的则都是系统的事。
重载函数的声明 函 数 重 载 C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。 函 数 重 载 C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。 函数重载:两个以上的函数,取相同的函数名,但形参的个数或类型不同,编译器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。其中的每个函数称为重载函数。 例: 形参类型不同 int add(int x, int y); float add(float x, float y); 形参个数不同 int add(int x, int y); int add(int x, int y, int z);
注意事项 函 数 重 载 重载函数的形参必须不同: 个数不同或类型不同。 函 数 重 载 重载函数的形参必须不同: 个数不同或类型不同。 编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。 int add(int x,int y); int add(int a,int b); 编译器不以形参名来区分 int add(int x,int y); void add(int x,int y); 编译器不以返回值来区分 不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆。这样不好: int add(int x,int y) { return x+y; } float add(float x,float y) { return x-y; }
例3-16 重载函数应用举例 编写三个名为add的重载函数,分别实现两整数相加、两实数相加和两个复数相加的功能。 #include<iostream.h> struct complex { double real; double imaginary; };
void main(void) { int m, n; double x, y; complex c1, c2, c3; int add(int m, int n); double add(double x, double y); complex add(complex c1, complex c2); cout<<"Enter two integer: "; cin>>m>>n; cout<<"integer "<<m<<'+'<<n<<"="<<add(m,n)<<endl;
cout<<"Enter two real number: "; cin>>x>>y; cout<<"real number "<<x<<'+'<<y<<"= "<<add(x,y) <<endl; cout<<"Enter the first complex number: "; cin>>c1.real>>c1.imaginary; cout<<"Enter the second complex number: "; cin>>c2.real>>c2.imaginary; c3=add(c1,c2); cout<<"complex number (" <<c1.real<< ',' << c1.imaginary <<")+("<<c2.real<<',' <<c2.imaginary<<")=("<<c3.real<<',' <<c3.imaginary<<")\n"; }
int add(int m, int n) { return m+n; } double add(double x, double y) { return x+y; } complex add(complex c1, complex c2) { complex c; c.real=c1.real+c2.real; c.imaginary=c1.imaginary+c2.imaginary; return c; }
运行结果: Enter two integer: 3 5 integer 3+5=8 Enter two real number: 2.3 5.8 real number 2.3+5.8= 8.1 Enter the first complex number: 12.3 45.6 Enter the second complex number: 56.7 67.8 complex number (12.3,45.6)+(56.7,67.8)= (69,113.4)
函数模板 重载函数,只是使用相同的函数名,每个函数的函数体仍要分别定义。 例 两个求绝对值的重载函数: 这两个函数只有参数类型不同,功能完全一样。若能写一段通用代码适用于多种不同数据类型,可提高代码重用性。通过函数模板可将多个重载函数归为一个。 int abs(int x) { return x<0?-x:x;} double abs(double x) { return x<0?-x:x;}
函数模板的声明 函 数 模 板 函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计。 声明方法: 函 数 模 板 函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计。 声明方法: template <typename 模板参数表> 函数定义 或 template<class 模板参数表> 函数定义
例3-17 求绝对值函数的模板 函 数 模 板 #include<iostream.h> 函 数 模 板 #include<iostream.h> template<typename T> T abs(T x) { return x<0?-x:x; } void main( ) { int n=-5; double d=-5.5; cout<<abs(n)<<endl; cout<<abs(d)<<endl; }
运行结果: 5 5.5 分析 编译器从调用abs( )时实参的类型,推导出函数模板的类型参数。例如,对于调用表达式abs(n),由于实参n为int型,所以推导出模板中类型参数T为int。 当类型参数的含义确定后,编译器将以函数模板为样板,生成一个函数: int abs(int x) { return x<0?-x:x; }
函数模板 函数模板定义不是一个实实在在的函数,编译系统不为其产生任何执行代码,该定义只是对函数的描述,表示它每次能单独处理在类型形式参数表中说明的数据类型。 当编译系统发现有一个函数调用:funcname(实参表);将根据实参表中的类型,确认是否匹配函数模板中对应的形式参数表,然后生成一个重载函数(模板函数)
#include<iostream.h> #include<string.h> template<class T> //template<typename T> T max(T a, T b) { return a>b?a:b; } char* max(char* a, char* b) { return (strcmp(a,b)>0?a:b); } 其名字与函数模板的名字相同,但操作不同,函数体中采用了字串比较函数,需用重载的方法把它们区分开,这就是重载模板函数。编译器首先匹配重载函数,然后再寻求模板的匹配。
void main() {cout<<"Max(3,5) is "<<max(3,5)<<endl; cout<<"Max('3','5') is "<<max('3','5')<<endl; cout<<"Max(\"Hello\",\"Gold\") is " <<max("Hello","Gold")<<endl; } 运行结果: Max(3,5) is 5 Max('3','5') is 5 Max("Hello","Gold") is Hello
函数模板与函数重载 函数模板适用于函数名相同,算法相同,参数个数相同,但类型不同的问题。 函数重载适用于函数名相同,算法相同,参数个数或类型不同的问题。 例:int f(int x,int y) int f(int x,int y) {return x+y;} {return x*y;} 这两个函数不是重载函数。
C++系统函数 使用C++系统函数 C++的系统库中提供了几百个函数可供程序员使用。 使用系统函数时要包含相应的头文件。 例如:求平方根函数(sprt)、求绝对值函数(abs)等。 使用系统函数时要包含相应的头文件。 例如:math.h
例3-18 系统函数应用举例 使用C++系统函数 题目: 分析: 从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值。 系统函数中提供了求正弦值、余弦值和正切值的函数:sin( )、cos( )、tan( ),函数的说明在头文件math.h中。
#include<iostream.h> #include<math.h> const double pi(3.14159265); void main( ) { double a,b; cin>>a; b=a*pi/180; cout<<"sin("<<a<<")="<<sin(b)<<endl; cout<<"cos("<<a<<")="<<cos(b)<<endl; cout<<"tan("<<a<<")="<<tan(b)<<endl; }
运行结果: 30 sin(30)=0.5 cos(30)=0.866025 tan(30)=0.57735
查找系统函数的使用说明 使用C++系统函数 查编译系统的库函数手册 查联机帮助——VC++6.0联机帮助的使用方法: help/Contents ->(“活动子集”栏)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 by Category
作 业 复习第三章,预习第四章 3-2, 3-13, 3-15 学习使用联机帮助系统查找系统函数