南开大学 高级语言程序设计(C++) 第5章函数及运算符重载_第1页
南开大学 高级语言程序设计(C++) 第5章函数及运算符重载_第2页
南开大学 高级语言程序设计(C++) 第5章函数及运算符重载_第3页
南开大学 高级语言程序设计(C++) 第5章函数及运算符重载_第4页
南开大学 高级语言程序设计(C++) 第5章函数及运算符重载_第5页
已阅读5页,还剩248页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、张莹张莹计算机与控制工程学院计算机与控制工程学院第第5章章 函数与重载函数与重载函数的说明与使用函数的说明与使用1函数的嵌套与递归函数的嵌套与递归2函数与运算符重载函数与运算符重载3函数与函数与C+程序结构程序结构41函数的使用与说明函数的使用与说明v【例例5.1】求三次方程的根。求三次方程的根。计算三次方程计算三次方程x3+px+q=0的一个实根的公式为的一个实根的公式为2332332)3()2(2)3()2(2pqqpqqxr= 将计算公式分解为如下步骤将计算公式分解为如下步骤 令实数令实数x xr rA+BA+B 令实数令实数A, B A, B 分别为实数分别为实数R,SR,S的立方根的

2、立方根 令令R = -q/2 + a, S = -q/2R = -q/2 + a, S = -q/2a a 令令a=a=sqrtsqrt(q/2)(q/2)* *(q/2)+(p/3)(q/2)+(p/3)* *(p/3)(p/3)* *(p/3(p/3) ) 函数的使用与说明函数的使用与说明v程序设计步骤程序设计步骤 输入方程的参数p和q,输出方程 计算变量a的值 计算变量R和变量S的值 计算变量A和变量B的值 分别对R和S求三次方根 求y的三次方根的迭代公式 计算Xr的值3函数的使用与说明函数的使用与说明v程序代码程序代码4#include/program5-1#include/use f

3、unction sqrtusing namespace std;void main(void)float p, q, xr; coutP;coutq;coutThe Equation is:;coutx3+px+q=0eps);A = croot;croot = S;doroot = croot;croot = (2*root + S/(root*root)/3;while(fabs(croot-root)eps);B = croot;xr = A + B;coutendlThe real root of the equation iseps);A = croot;croot = S;doro

4、ot = croot;croot = (2*root + S/(root*root)/3;while(fabs(croot-root)eps);B = croot;函数的使用和说明函数的使用和说明v函数的引入函数的引入 程序中功能相同,结构相似的代码段可以用函数进行描述 程序的功能相对独立,用来解决某个问题 具有明显的入口和出口 入口:参数 出口:返回值7函数的使用和说明函数的使用和说明8函数的使用和说明函数的使用和说明v声明函数声明函数cuberoot 参数为待求根的变量v函数函数cuberoot的定义如下:的定义如下:9float cuberoot(float x)/精确到小数点后精确到小

5、数点后6位位float root , croot;const float eps=1e-6;croot=x;do root=croot; croot=(2*root+x/(root*root)/3; while(fabs(crootroot)eps);return croot;函数的使用和说明函数的使用和说明v函数的作用函数的作用 实现程序功能的模块化 实现程序结构的简化 实现程序代码的重用v函数的应用场景函数的应用场景 包含多处功能相同的代码 处理数据的类型、处理过程相同或相似 代码段具有代表性或特殊含义10函数的使用和说明函数的使用和说明v用函数的思想实现最简单的用函数的思想实现最简单的C

6、+程序程序11#inlcudeusing namespace std;void printString();/函数原型函数原型int main() printString();/调用函数调用函数printString return 0;void pirntString()/函数定义函数定义 cout“Hello!”endl; return; /函数返回函数返回函数的说明与使用函数的说明与使用v函数的使用步骤函数的使用步骤 先说明,后调用 程序中必须包含函数的说明及定义v函数的说明方式函数的说明方式 函数原型 函数原型必须出现在调用函数之前 函数定义可以出现在程序的任何合适的地方 函数定义 函数

7、定义必须出现在调用函数之前12函数的说明与使用函数的说明与使用v主调函数主调函数 函数体中调用其它函数v被调函数被调函数 被其它函数调用的函数v上述概念是相对的概念,一个函数既可以调用其上述概念是相对的概念,一个函数既可以调用其它函数(包括该函数自身),亦可以被其它函数它函数(包括该函数自身),亦可以被其它函数调用(包括该函数自身)调用(包括该函数自身)13从从函数形式函数形式划分划分,可分为无参函,可分为无参函数与有参函数两数与有参函数两类类从从使用角度使用角度划分划分,可将函数分为,可将函数分为:系统预定义系统预定义的的标准库函数(如标准库函数(如,sinsin,absabs等)等),以及

8、由,以及由用户自用户自定义定义的函数的函数函数分类函数分类方法方法14函数的说明与使用函数的说明与使用函数的说明与使用函数的说明与使用v标准库函数标准库函数 程序中可直接使用(调用)系统预定义的标准库函数,但要求在调用前使用编译预处理指令include将对应的头文件包含进来v用户自定义函数用户自定义函数 由用户自定义的函数与系统预定义的标准库函数的不同点在于,自定义函数的函数名、参数个数、函数返回值类型以及函数所实现的功能等都完全由用户程序来规定(指定)15函数的说明与使用函数的说明与使用v标准库函数标准库函数 标准输入输出流() setw()、setwidth()等 标准文件流() open

9、()、close()等 标准字符串处理函数() strcpy()、strcmp()等 标准数学函数() sqrt()、pow()等16函数的说明与使用函数的说明与使用v用户自定义函数用户自定义函数 普通函数 内联函数 类的成员函数 类的友元函数 虚函数 17函数的说明与使用函数的说明与使用v无参函数无参函数 调用它们时不需要提供实际参数 函数原型的一般形式( ); 函数定义的一般形式( ) 通常用来实现某种特定的功能 不需要进行数据的传递 处理的数据通常与主调函数无关18函数的说明与使用函数的说明与使用v无参函数无参函数 【例5.2】定义一个函数,实现打印10个“*”的功能19void pri

10、ntStar() for(int i=0;i10;i+) cout“*”; coutendl;函数的说明与使用函数的说明与使用v有参函数(有参函数(带有参数的用户自定义函数带有参数的用户自定义函数) 进行调用时,必须提供所需个数的且具有相匹配数据类型的实际参数 函数原型的一般形式 (); 定义的一般形式 () 通过调用处提供的不同实参值来计算出其对应的函数值、或实现某种与传递过来的那些不同实参值有关的某种功能(返回值为void)。20函数的说明与使用函数的说明与使用v有参函数有参函数 【例5.3】定义一个函数,控制输出“*”的个数21void printStar(int k) for(int

11、i=0;ik;i+) cout“*”; coutendl;函数函数的使用的使用调用调用说明与定义说明与定义参数参数重载重载返回返回内联函数内联函数22函数的说明与使用函数的说明与使用函数的说明与使用函数的说明与使用23函数的说明与使用函数的说明与使用v函数的说明和定义函数的说明和定义 在C+程序中,函数有两种说明方式 函数原型 在调用函数之前说明函数原型 函数定义 在调用函数之前对函数进行定义24函数的说明与使用函数的说明与使用v函数原型函数原型 也称为函数声明 用来指明函数的名称、参数以及返回值类型 函数原型格式为:(); 例如int add(int a, int b); inline vo

12、id swap(float &s, float &t);void print (char *);25函数的说明与使用函数的说明与使用v函数原型函数原型 属性说明:可缺省,一般可以是下面的关键字之一 inline:表示该函数为内联函数 static:表示该函数为静态函数 virtual:表示该函数为虚函数 friend:表示该函数为某类(class)的友元函数 返回值类型 函数处理得到的结果的数据类型 函数名 标识符26函数的说明与使用函数的说明与使用v函数原型函数原型 参数表 空参数表void printroot(); void型参数void printroot(void);

13、,* float cuberoot(float);float cuberoot(float x);27函数的说明与使用函数的说明与使用v函数定义函数定义 函数定义与函数原型的主要区别是它还包括函数体,其格式为() 函数体 复合语句即程序块,由完成函数功能所需的全部语句构成28函数的说明与使用函数的说明与使用v函数的说明和定义函数的说明和定义 “函数原型”的说明方式 “函数定义”的说明方式29#includeusing namespace std;void printStar(int);/函数原型函数原型int main() printStar(10);/调用函数调用函数void printSt

14、ar(int k)/函数体函数体#includeusing namespace std;void printStar(int k)/函数定义函数定义int main()printStar(10); /调用函数调用函数函数的说明与使用函数的说明与使用v函数的说明和定义函数的说明和定义 两种说明方式的区别 函数原型的参数表中,参数名可以省略;函数定义的参数表中,必须给出参数名(省略参数名为无名参数) 函数原型的函数体,可以出现在函数调用之后;函数定义的函数体,必须出现在调用之前 函数原型的参数表后面加分号“;”,函数定义的参数表后面是函数体,即花括号“” 函数定义不能出现在任何函数体中,函数原型可

15、以出现在其它函数体中30函数的说明与使用函数的说明与使用v函数的调用函数的调用 函数调用是已定义函数的一次实际运行,与某类型的一个变量和后文中某类的一个对象类似,函数调用是函数定义的一个“实例” 在C+程序中,除main函数外,其它任一函数的执行都是通过在main函数中直接或间接地调用该函数而引发的。调用一个函数就是去执行该函数之函数体的过程31函数的说明与使用函数的说明与使用v函数的调用函数的调用 函数调用过程32函数的说明与使用函数的说明与使用v函数的调用函数的调用 函数调用的执行顺序 根据调用语句中的函数名在整个程序中搜索同名函数定义; 对实参数的参数个数,类型,顺序进行核对,判定是否与

16、函数定义中的形参表对应一致 根据参数的类型(值参数或引用参数)进行值参数的值传递或引用参数的换名 运行函数体代码 返回调用点,并返回所要求的函数值33函数的说明与使用函数的说明与使用v函数的调用函数的调用 无参函数调用格式() 例如:printStar(); 有参函数调用格式 ( ) 例如:printStar(26);34函数的说明与使用函数的说明与使用v函数的返回函数的返回 函数的返回表示函数执行结束,将执行结果(无论是否有具体的数据)返回到调用函数的地方 函数返回时完成的任务 把运行控制从函数体返回到函数调用点 根据返回值要求,返回所需要的数据值 返回值类型 void 数值型 引用类型35

17、函数的说明与使用函数的说明与使用v函数的返回函数的返回 返回值类型 空型(void) 如果函数无值返回,应说明为void 类型。未作类型说明的函数,系统认为是int 类型函数,应返回一整型值 值型:返回一个具有类型的值,包括int、float、char、bool等 当函数要返回的值不止一个时,情况比较复杂,一般它可以以结构或类的形式,也可以以结构,数组或对象指针类型方式实现,这样的实例在后面的章节可以见到 引用类型:详见第6章36函数的说明与使用函数的说明与使用v函数的返回函数的返回 函数返回用return语句表示 return语句有如下几种写法return;/函数返回值类型为空(void)r

18、eturn ;/与函数返回值的类型一致return ();/与前一种写法等价37第一种格式的第一种格式的returnreturn用于立即从被调函数中返回用于立即从被调函数中返回, , 当函数类型为当函数类型为voidvoid时,时,应使用这种格式的返回语句。应使用这种格式的返回语句。当函数类型为当函数类型为非非voidvoid型时,应使用第二或第三种格式的型时,应使用第二或第三种格式的returnreturn语句,此两语句,此两种格式的语句效果完全相同(可将第二种格式看成是第三种格式的省略形种格式的语句效果完全相同(可将第二种格式看成是第三种格式的省略形式),系统此时都将计算出表达式的值,并式

19、),系统此时都将计算出表达式的值,并“携带携带”该值立即从被调函数中该值立即从被调函数中返回返回函数的说明与使用函数的说明与使用v函数应用举例函数应用举例 【例5.4】设 f(x) = (x*x+x+1)/2-5.5 求z = (f(2.5)+2*f(6)/f(4.3),并显示结果z。 对任意输入的一个实数a,求出f(a)并显示 分析: 输入(参数):x 输出(返回):函数的运算结果 函数原型:double f (double);38函数的说明与使用函数的说明与使用v函数应用举例函数应用举例 【例5.4】函数定义double f (double x)double y;y=(x*x+x+1)/2

20、-5.5;return y; /对非对非void类型的函数,必须有一个类型的函数,必须有一个 /return语句,由它返回函数值语句,由它返回函数值 39函数的说明与使用函数的说明与使用v函数应用举例函数应用举例【例例5.4】程序程序#includeusing namespace std;double f (double);int main()double z,a;z=(f(2.5)+2*f(6)/f(4.3); /调用自定义函数调用自定义函数fcoutz=zendl;couta;coutf(a)=f(a)endl; /算出算出f(a)并输出并输出return 0;double f (doub

21、le x)/函数函数f的定义的定义40函数的说明与使用函数的说明与使用v函数应用举例函数应用举例【例例5.4】程序(不带函数原型)程序(不带函数原型)#includeusing namespace std;double f (double x)/函数函数f的定义的定义int main()double z,a;z=(f(2.5)+2*f(6)/f(4.3); /调用自定义函数调用自定义函数fcoutz=zendl;couta;coutf(a)=f(a)endl; /算出算出f(a)并输出并输出return 0;41函数的说明与使用函数的说明与使用v函数应用举例函数应用举例 【例5.4】点评 f(

22、)函数体内的3行也可用如下的一行来代替 return (x*x+x+1)/2-5.5); return句括号内表达式的值,即为整个函数的返回值。 return句也可使用另一格式,即可以不括起表达式: return (x*x+x+1)/2-5.5; /OK!42 f=(xf=(x* *x+x+1)/2-5.5; x+x+1)/2-5.5; 不可给函数名不可给函数名f f赋值。赋值。 return (f); return (f); 返回值类型应该是返回值类型应该是doubledouble,而非指针类型(函数名相而非指针类型(函数名相当于一个指针)。当于一个指针)。 f(x)=(xf(x)=(x*

23、*x+x+1)/2-5.5; x+x+1)/2-5.5; 赋值号左端非变量(也即赋值号左端非变量(也即f(x)f(x)非左值)。非左值)。 函数的说明与使用函数的说明与使用v函数的参数函数的参数 C+语言的函数分为无参函数和有参函数 函数的参数 无参数 一个参数 多个参数 函数参数表的写法 一般写法 省略参数名(无名参数) 参数赋初值43函数的说明与使用函数的说明与使用v函数的参数函数的参数 形参和实参 函数说明中的参数称为形式参数(形参),函数调用中的参数称为实际参数(实参) 实参表在参数个数、参数顺序、以及参数类型等方面要与被调函数的形参表之间有一个一一对应的相互匹配关系 编译器将根据参数

24、的顺序,来逐一实现实参与对应形参的“结合”,而后执行一遍函数体(而完成本次的函数调用)44函数的说明与使用函数的说明与使用v函数的参数函数的参数 无名参数 函数定义中,只有类型,没有名称的参数int f(int a,int b)return a+b*b;int f(int a,int b,int)return a*a+b; 两个不同的函数同名,但由于第二个函数包含一无名参数,使得在调用时能够被区分,f(x,y)是第一个函数的调用,f(x,y,0)是第二个函数的调用45函数的说明与使用函数的说明与使用v函数的参数函数的参数 可缺省参数(参数的默认值) 允许在函数定义处为连续若干个参数设置默认值(

25、也称缺省值) 若调用处缺省了某个或某些实参的情况下,系统将自动使用那些在函数定义处给定的参数默认值 例如,在定义函数func时为其三个参数设置默认值:46#includeusing namespace std;void func(int a=11, int b=22, int c=33) /为参数为参数a、b、c设置了默认值设置了默认值11、22与与33couta=a, b=b, c=cendl; 函数的说明与使用函数的说明与使用47int main() func(); /调用时缺省了调用时缺省了3个实参,将使用个实参,将使用 /定义处给定的那定义处给定的那3个相对应的参数默认值个相对应的参数

26、默认值 func(55); /调用时缺省了后调用时缺省了后2个实参,将使用个实参,将使用 /定义处给定的那后定义处给定的那后2个对应参数默认值个对应参数默认值 func(77,99);/调用时缺省了最后调用时缺省了最后1个实参,将使用个实参,将使用 /定义处给定的那最后定义处给定的那最后1个参数默认值个参数默认值 func(8,88,888); /调用时没缺省任一个实参,调用时没缺省任一个实参, /系统将不使用定义处给定的任一个参数默认值系统将不使用定义处给定的任一个参数默认值 return 0; 函数的说明与使用函数的说明与使用运行结果:运行结果:a=11, b=22, c=33a=55,

27、b=22, c=33a=77, b=99, c=33a=8, b=88, c=88848函数的说明与使用函数的说明与使用v函数的参数函数的参数 可缺省参数(参数的默认值) 只能为函数最后面的连续若干个参数设置默认值,且在调用处也只能缺省后面的连续若干个实参 例如:49 void func(int a, int b=2, int c=3); /OK! void func(int a=1, int b, int c=3); /ERROR! void func(int a=1, int b=2, int c); /ERROR! 对第一个函数说明,采用如下的调用语句:对第一个函数说明,采用如下的调用语

28、句: func(1, 22, 333); /OK! 调用时给出所有实参调用时给出所有实参 func(); / ERROR! 参数参数a没有默认值没有默认值 func(10,20); /OK! 参数参数c默认为默认为3 func(5, ,9);/ERROR!调用处也只能缺省后面的连续若干个实参调用处也只能缺省后面的连续若干个实参 函数的说明与使用函数的说明与使用v函数调用过程中参数的传递函数调用过程中参数的传递 一般传递过程(赋值传递) 发生函数调用,转到函数体执行 根据函数的形参,分配内存空间 将实参赋值给形参,即为形参分配的存储空间赋值,此时实参在函数体内失效,形参有效 函数执行完毕,返回到

29、主调函数,形参所占的空间自动回收,形参失效50函数的说明与使用函数的说明与使用v函数调用过程中参数的传递函数调用过程中参数的传递 按地址传递过程 发生函数调用,转到函数体执行 根据代表地址的参数,在以该参数为首地址的空间中进行各种处理 数组名 指针 引用 函数执行完毕,无论是否有返回值,函数体对内存空间进行的修改将保留,对主调函数仍然有效51函数的说明与使用函数的说明与使用v函数的参数函数的参数 能够作为函数参数的数据类型 基本类型及其派生类型 赋值参数传递 数组、指针、引用等导出类型、 按地址传递参数 类类型、结构类型、联合类型等用户定义类型 赋值参数传递 按地址参数传递52函数的说明与使用

30、函数的说明与使用v函数的参数函数的参数 一维数组作为函数的参数,数组大小可以指定,也可以不指定,但是多维数组除了第一维之外,其它维的大小必须指定 函数原型int searchArray(int ,int);int searchArray(int 10,int); 函数定义int searchArray(int a,int b)/写为a10也可以return ab;53函数的说明与使用函数的说明与使用v函数的参数函数的参数 多维数组(以二维数组为例)作为函数的参数,参数表的数组参数可以写为int a10int a1010但不可以写为:int a;int a20; 一维数组以及多维数组的第一维大小

31、,形参、实参可以不对应 实参为a10,形参可以定义为x654函数的说明与使用函数的说明与使用v函数的参数函数的参数 数组作为函数参数,是将实参数组的首地址传递给形参,而不是将数组的所有元素传递给形参 在函数体中,根据形参数组首地址和下标指示的偏移量访问数组元素 能够表示地址的数据类型 数组 首地址(第一个元素的地址) 指针 引用55函数的说明与使用函数的说明与使用v函数的参数函数的参数 【例5.5】一维数组作参数举例:设计函数,实现两个字符数组的连接。56#includeusing namespace std;void strcat(char s,char ct) int i=0,j=0; w

32、hile (si!=0) i+; while (ctj!=0) si+=ctj+; si=0;函数的说明与使用函数的说明与使用v函数的参数函数的参数 【例5.5】主函数部分57int main (void) char a40=李明李明; char b20=是东南大学学生是东南大学学生; strcat(a,b);/实参为数组名实参为数组名 coutaendl;/打印字符数组打印字符数组a return 0;函数的说明与使用函数的说明与使用v函数的参数函数的参数 【例5.6】多维数组做函数参数举例:设计函数,实现矩阵的转置和矩阵乘法,并在主函数中验证 用二维数组存储矩阵 由于数组作为参数,传递的是

33、数组的首地址,因此,在函数中对作为形参的数组进行的操作,能够直接反映到实参数组中58函数的说明与使用函数的说明与使用 【例5.6】函数原型及主函数59#includeusing namespace std;void inverse(int 36, int 63);/转置矩阵转置矩阵void multi(int 63,int 34,int 64);/矩阵乘法矩阵乘法void output(int 64); /矩阵输出矩阵输出int main() int middle63, result64; int matrix136=8,10,12,23,1,3,5,7,9,2,4,6,34,45,56,2,

34、4,6; int matrix234=3,2,1,0,-1,-2,9,8,7,6,5,4; inverse(matrix1,middle);/实参为数组名实参为数组名 multi(middle,matrix2,result); output(result); return 0;函数的说明与使用函数的说明与使用 【例5.6】转置和乘法函数定义60void inverse(int matrix136,int middle63)/转置转置 int i,j; for (i=0;i3;i+) for (j=0;j6;j+) middleji=matrix1ij; return;void multi(in

35、t middle63,int matrix234,int result64) int i,j,k; /矩阵乘法矩阵乘法 for (i=0;i6;i+) for (j=0;j4;j+) resultij = 0; for (k=0;k3;k+) resultij+=middleik*matrix2kj; return; 函数的说明与使用函数的说明与使用 【例5.6】矩阵输出函数定义61void output(int result64) /矩阵输出矩阵输出 cout resultn; int i,j; for (i=0;i6;i+) for (j=0;j4;j+) cout setw(4)resu

36、ltij ; coutn; return; 函数的说明与使用函数的说明与使用v函数的参数函数的参数 变量的引用作为函数的形式参数 赋值形参:未被说明为引用(&)的参数 赋值形参和实参直观上是一致的,实际是不同的 在内存中的地址不同 引用形参变量的引用 为变量起“别名” 在作为形参的变量前加符号“&” 与变量的内存地址相同 需要在函数中改变实参值并将变化反映到主调函数的时候,用作为实参的变量引用是一种方法 还可以用指针做形参操作变量地址62函数的说明与使用函数的说明与使用v函数的参数函数的参数 赋值形参 赋值形参的函数调用过程和参数传递机制 在执行函数调用时,在检查函数名及参数表

37、之后,为赋值参数分配内存 计算各对应的实参表达式,并把计算的值赋给刚刚创建的参数变量 开始函数体的运行。 凡是赋值形参,在函数的每次调用时,都必须为每一个赋值形参创建一个新的参数变量。63函数的说明与使用函数的说明与使用v函数的参数函数的参数 赋值形参 实参表达式:函数调用语句中,与赋值形参相对应的实参可以是指定类型的常量、变量或表达式。在执行函数调用时应把该表达式的值计算出来,作为初值赋给刚刚为赋值形参创建的参数变量。这是赋值调用方式名称的由来。 为赋值形参创建的参数变量是局限于函数体运行的局部变量,它作为该形参的一个实例,参加函数体程序块的这次运行,一旦运行完毕,这个参数变量就被撤消64函

38、数的说明与使用函数的说明与使用v函数的参数函数的参数 实参表达式作参数 例如,函数原型:int add(int a, int b); 调用函数时,可以采用如下方式:int main()int x = 5;int c=add(1, 4*x+2);/实参为表达式65函数的使用和说明函数的使用和说明v函数的参数函数的参数 【例5.7】编写函数,实现两个整数的交换 输入两个整数 输出初始值 交换 输出交换后的值66void swap (int x, int y) int temp = x; x = y; y = temp;函数的说明和使用函数的说明和使用v【例例5.7】主函数部分主函数部分#inlcu

39、deusing namespace std;void swap (int x, int y).int main()int a,b;cinab;cout“a=“a“b=“bendl;swap(a,b);cout“a=“a“b=“bendl;return 0;67函数的说明与使用函数的说明与使用v【例例5.7】分析分析 程序运行结果显示,a和b的值没有进行交换! 主函数运行时,为变量a和b分配存储空间 调用函数swap时,为变量x和y分配存储空间 函数体中交换了x和y存储单元的值 a和b所在存储单元的值没有交换 输出的仍然是a和b所在存储单元的值68函数的说明与使用函数的说明与使用v函数的参数函数

40、的参数 引用形参 函数定义的参数表中,名字前加上符号的参数为引用形参。例如void swap(int a,int b); 引用形参在调用过程中的参数传递机制 函数的调用语句中对应于引用形参的实参必须是同一类型的变量,非变量的表达式则不允许。 参数传递的内容不是实参的值,而是地址,其实际的效果是令对应的引用形参在调用过程中,作为一个变量名指向作为实参的这个变量,在引用调用过程中并不创建新的参数变量。这一点有别于赋值调用69函数的说明与使用函数的说明与使用v函数的参数函数的参数 引用形参 引用形参在调用过程中的参数传递机制 在函数体程序块的运行中,引用形参的每次出现,由于它现在已经是指向实参变量,

41、因此相当于全用实参变量所代替。即起到了所谓的“换名”的作用。 在函数体程序运行结束,控制转回调用点时,该引用形参与实参变量的对应关系也就终止了。但是在调用过程中对于这个实参变量的所有处理和操作的结果,却保留下来。这一点也是区别于赋值调用的 70函数的说明与使用函数的说明与使用v函数的参数函数的参数 引用形参 设计函数在下面两种情形时,建议采用引用参数 需要改变某些变量的值(上述函数swap 就是一例) 对于占内存较多的数据参数,为了不另建新的参数变量以节省内存 为了保证实参不在函数中被修改,可在形参说明中加上const 说明,例如: complex add(const complex&

42、 a,const complex& b); 而对于赋值形参,则无此必要。71函数的说明与使用函数的说明与使用v函数的参数函数的参数 赋值调用 所调用的函数参数为赋值参数 引用调用 所调用的函数参数为引用参数 举例void printStar(int k, int n);/它所用的两个参数均为赋值参数它所用的两个参数均为赋值参数void swap(int& x, int& y); /它所用的两个参数均为引用参数它所用的两个参数均为引用参数int myFunc(int a, float& b);/它所用的第一个参数为赋值参数,另一个为引用参数它所用的第一个参数为赋值

43、参数,另一个为引用参数72函数的说明与使用函数的说明与使用v函数的参数函数的参数 【例5.7】中的交换函数,采用引用作为参数void swap (int &x, int &y) int temp = x; x = y; y = temp; 引用参数&x和&y是实参的别名,函数中对引用形参的操作实际上就是对实参的操作73函数的说明与使用函数的说明与使用v函数的参数函数的参数 引用调用方式,实际上传递的是参数的地址(实参的地址),函数体中对形参的操作实际上是对实参地址的操作 希望在函数中修改实参值并反映到主调函数中的时候,采用引用调用方式 指针、数组等能够表示地址的

44、数据类型作为形参,可以起到与引用形参相同的作用 【例5.5】和【例5.6】是数组作为参数,在函数体中修改数组并反映到主调函数的例子74函数的说明与使用函数的说明与使用v函数的参数函数的参数 从赋值或引用参数的角度看函数的调用过程 判断形参是赋值参数还是引用参数 将对应实参表达式的值赋给赋值形参(若参数为赋值参数的话); 用实参变量替换相应的形参(若参数为引用参数的话); 按各形参的“当前值”(或已被“赋值”,或已被“换名”)去执行一遍函数体并返回调用处75函数的说明与使用函数的说明与使用v函数的参数函数的参数 赋值调用和引用调用传值方式的区别 通过赋值参数来传值的方式是一种“单向传值”方式,它

45、只可向被调函数的形参“传入”值,而不可通过该形参“传出”值 通过引用参数来传值的方式是一种“双向传值”方式,它不仅可向被调函数的形参“传入”值,而且还可通过该形参“传出”值 76函数的说明与使用函数的说明与使用v函数的参数函数的参数 【例5.8】赋值调用和引用调用举例#includeusing namespace std;void swap(int& a, int& b, int c, int d); /函数原型函数原型 /前两个为引用参数,可前两个为引用参数,可“双向传值双向传值” /后两个为赋值参数后两个为赋值参数77函数的说明与使用函数的说明与使用int main() i

46、nt i=1, j=2, k=77, n=88;cout“ -In main, befor calling f1 -endl;couti, j, k, n = i j k nendlendl;swap (i, j, k, n); /注意,调用后实参变量注意,调用后实参变量i、j的值进行了改变的值进行了改变/而而k与与n的值并不改变的值并不改变cout- In main, after calling f1 -endl;couti, j, k, n = i j k nendl;78函数的说明与使用函数的说明与使用void swap (int& a, int& b, int c, i

47、nt d) /* 前两个为引用参数,后两个为赋值参数。对引用参前两个为引用参数,后两个为赋值参数。对引用参数而言,调用时,将用对应实参变量来替换它们。即是说,数而言,调用时,将用对应实参变量来替换它们。即是说,被调函数中对形参值的使用与改变,就是对主调函数中调被调函数中对形参值的使用与改变,就是对主调函数中调用语句处所对应实参变量值的直接使用与改变(用语句处所对应实参变量值的直接使用与改变(“双向传双向传值值”) */ cout- Enter f1 -endl; couta, b, c, d = a b c dendlendl; int tmp;tmp=a; a=b; b=tmp; /交换交换

48、a与与b的值的值tmp=c; c=d; d=tmp; /交换交换c与与d的值的值cout- In the end of f1 -endl; couta, b, c, d = a b c dendlendl; 79函数的说明与使用函数的说明与使用程序运行结果程序运行结果- In main, befor calling f1 -i, j, k, n = 1 2 77 88 - Enter f1 -a, b, c, d = 1 2 77 88 - In the end of f1 -a, b, c, d = 2 1 88 77 - In main, after calling f1 -i, j, k

49、, n = 2 1 77 8880函数的说明与使用函数的说明与使用v函数的重载函数的重载(后面具体讲解)(后面具体讲解) 多个函数使用相同的函数名 函数重载必须满足下列条件之一 参数表中对应的参数类型不同 参数表中参数个数不同 参数表中不同类型参数的次序不同 例如,三个同名函数可以声明为:printStar();printStar(int);printStar(int,int);81函数的说明与使用函数的说明与使用v内联函数内联函数 可在一般的函数说明前冠以关键字inline,称这样的函数为内联函数。 说明方式:inline () 82函数的说明与使用函数的说明与使用v内联函数内联函数 在编译

50、过程中,凡内联函数,系统均把它的执行代码插入到该函数的每个调用点处(以取代那一函数调用),从而使程序执行过程中,每次对该函数调用时不需控制转移,可节省执行时间 由于每个调用点处均出现那一函数的执行代码拷贝,相对来说使用内联函数后会扩大其代码空间83函数的说明与使用函数的说明与使用v内联函数举例内联函数举例#include inline int max(int x, int y) /内联函数内联函数maxreturn(xy?x:y);void main()int a,b; coutab;coutmax(a,b)=max(a,b)1)value *= number;number-;return v

51、alue;89函数的说明与使用函数的说明与使用v【例例5.9】主程序部分主程序部分#include using namespace std;long factorial(int number); /函数原型函数原型int main()int i,selections,elements; /计算计算A(elements,selections)/以及以及C(elements,selections)coutselections; /输入整数输入整数selectionscoutelements; /输入整数输入整数elementsdouble answer = elements;int ele = e

52、lements; 90函数的说明与使用函数的说明与使用answer = factorial(elements) / factorial (elements-selects);cout“A(”elements“,”selections“)=”answerendl; /输出排列数输出排列数A(ele,sel)之结果之结果/组合数组合数C的求法:的求法:answer/=factorial(selections); coutC(elements,selections)=answerendl; /输出组合数输出组合数C(ele,sel)之结果之结果return 0;91第第5章章 函数与重载函数与重载函

53、数的说明与使用函数的说明与使用1函数的嵌套与递归函数的嵌套与递归2函数与运算符重载函数与运算符重载3函数与函数与C+程序结构程序结构492函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 一个函数的函数体中包含一个或多个函数调用语句,即称为函数嵌套 嵌套的含义是,如果函数A 要调用函数B,也就是说,函数A 的定义要依赖于函数B 的定义。因此函数B 的定义或函数B 的原型必须出现在函数A 的定义语句之前。 另一方面,函数A调用函数B,在调用A 的过程中,即执行A 的函数体过程中,调用B,也就是中途把程序控制转到B 的函数体,在执行结束后再返回到A 的函数体中93函数的嵌套与递归函数的嵌套与

54、递归v函数的嵌套函数的嵌套 示意图94函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用所占用的内存空间(如赋值参数的创建等等)用堆栈(stack)的方式管理。一般这种堆栈所分配的空间是有限的,因此函数互相嵌套的层数也是有限的,依编译系统不同,其允许的嵌套层数也可能不同95函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构96函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 主函数运行时,栈区的情况97函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构98函数的嵌套与递归函数的嵌

55、套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 发生函数调用时(调用a函数),“保护”主函数当前的运行状态,记录被调函数的返回地址99函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构100函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 运行a函数,记录a函数的参数和局部变量的值101函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构102函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 调用b函数,保护a函数的当前状态,记录b函数的返回地址103函

56、数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构104函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 运行b函数,记录b函数的参数和局部变量的值105函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构106函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 b函数运行结束并返回,b函数的变量和参数失效,变量和参数由栈区“弹出”,根据b函数的返回地址返回,读取a函数的运行状态继续运行a函数107函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程

57、中的栈结构108函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 函数嵌套调用过程中的栈结构 a函数运行结束并返回,a函数的变量和参数失效,变量和参数由栈区“弹出”,根据a函数的返回地址返回,读取主函数的运行状态继续运行主函数109函数的嵌套与递归函数的嵌套与递归v函数的嵌套函数的嵌套 【例5.10】编写程序,用冒泡排序的算法对数组中的元素按照由小到大的顺序进行排序 输入数组 调用排序函数进行排序 参数:待排序的数组 返回:空 由于参数为数组,即数组的首地址,函数体中处理数组的地址,因此,对数组的修改可以直接反映到主调函数中 输出排序后的数组110函数的嵌套与递归函数的嵌套与递归v【例例

58、5.10】函数函数bubSort代码代码void bubSort(int a,int n)for(int i=0;ii;j-)if(ajaj-1)swap(aj,aj-1);111函数的嵌套与递归函数的嵌套与递归v【例例5.10】函数函数swap代码代码void swap (int &x, int &y)int temp = x;x = y;y = temp;112函数的嵌套与递归函数的嵌套与递归v【例例5.10】主程序代码主程序代码#include#include#include#includeusing namespace std;const int n = 100;int

59、 main()int bn;srand(unsigned)time(NULL);for(int i=0;in;i+)bi = rand();113函数的嵌套与递归函数的嵌套与递归bubSort(b,n);for(i=0;in;i+)coutsetw(5)bi;if(i%10=0)coutendl;return 0;/*设计函数,分别按产生顺序和排序后的顺序输出设计函数,分别按产生顺序和排序后的顺序输出随机数,输出宽度为随机数,输出宽度为5,每输出,每输出10个数换行个数换行*/114函数的嵌套与递归函数的嵌套与递归v函数的递归函数的递归 C+允许函数自己调用自己(如A函数可以调用A函数本身,称

60、为直接递归)。也允许A函数调用B函数,而后B函数又调用A函数(从而形成间接递归)。但不论使用哪种递归,程序员都应保障递归函数在执行若干次后能够“退出”递归(不再进行递归调用,也即能够实现递归出口)115函数的嵌套与递归函数的嵌套与递归v函数的递归函数的递归 递归函数的执行分为“递推”和“回归”两个过程,这两个过程由递归终止条件控制,即逐层递推,直至递归终止条件,然后逐层回归。每次调用发生时都首先判断递归终止条件。116函数的嵌套与递归函数的嵌套与递归v函数的递归函数的递归 递归调用同普通的函数调用一样,每当调用发生时,在栈中分配单元保存返回地址以及参数和局部变量;而与普通的函数调用不同的是,由于递推的过程是一个逐层调用的过程,因此存在一个逐层连续的参数入栈过程,直至遇到递归终止条件时,才开始回归,这时才逐层释放栈空间,返回到上一

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论