




已阅读5页,还剩87页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
面向对象程序设计(二),吕俊白,第五章函数机制,主要内容:函数的概念;函数的声明、定义与调用;函数参数;递归函数;函数重载main函数参数;指针与函数;内联函数重点:函数的声明、定义与调用的语法;函数的参数传递。递归函数及其应用;函数重载;指针与函数,5.1函数的基本概念,函数是C+程序结构中的基本单位。1什么叫函数把实现特定功能的相关语句组织在一起,并给出相应的名称,就称为函数。2函数的作用()便于程序实现;()减少程序的重复编写;()使程序结构清晰、增强易读性。,3函数的分类,通常分为:标准库函数和用户自定义函数两大类。标准库函数(简称为库函数):是C+系统提供的可以为任何程序所使用的公共函数。用户自定义函数:是程序员编写的用于完成指定任务的函数。,5.2函数原型,函数在使用之前要先声明。C+中函数的声明总是由函数原型构成的。1什么叫函数原型函数原型是一条特殊的说明语句,用来声明程序中将调用的函数。函数原型由函数返回类型、函数名和参数表构成,以分号结束,并且一般放在程序的顶部。,2函数原型的语法,();例如:doublemax(double,double);注意:(1)参数类型表给出函数所有参数的数据类型(无须给出参数名);(2)函数原型和函数定义在函数类型(返回值类型)、函数名以及参数表这三部分上必须完全一致,否则就会发生编译错误;(3)标准库函数的原型都在对应的头文件中(*.h)。如果程序中使用到某些标准库函数,必须在程序顶部使用#include命令将对应的头文件包含进来。,3函数原型的作用,作用:在程序中加入函数原型有利于保证函数定义和调用上的一致性。一致性:是指定义和调用时函数类型、参数个数和参数类型方面的一一对应。当程序中加入函数原型,一旦函数原型与函数调用或函数定义不同,编译系统会报告出错,并给出相应的出错信息。,5.3函数定义,在程序中使用函数必须先定义后使用。1语法(参见P:136)C+的函数由函数头部(函数原型)和函数体两部分组成。其语法描述为:()/函数头部/函数体;,解释:,类型标识符:用来说明函数的返回类型,即:函数调用所返回的结果的数据类型。C+语言规定除了特别情形之外,所有函数都必须在说明中指出返回类型。如果省略类型标识符,则缺省类型是int;如果函数无值返回,应说明为void类型。例如:voidprint();/返回类型是voiddoublemax(double,double);/返回类型是double注意:函数的返回类型为非void型时,函数体要用return返回值,并且return语句中表达式的类型必须与函数的返回类型相一致。,解释:,函数名:用来给函数命名,C+用标识符来作为函数名。参数表:用来说明函数的参数,函数的参数是函数所提供的数据接口,用来实现调用者与被调用者之间的数据传递。参数说明必须给出参数的名称、类型,允许一个函数包含一到多个参数,参数之间用逗号分隔。函数定义中的参数称为“形式参数”;调用函数时所提供的参数称为“实在参数”。注意:形参只能是变量。例如:doublemax(doublex,doubley)max带有两个参数x和y,类型都是双精度型。,解释:,数据说明部分:在函数体内,用来说明在函数内部有效的数据对象(变量等),如果函数没有内部数据,则此部分可以省略。在函数内部定义的变量称为局部变量。局部变量仅在定义它的函数内部有效,局部变量未经初始化,其值总是不确定的。执行部分:在函数体内,由若干可执行语句组成。该部分不能省略。注意:C+不允许函数定义嵌套,即:在函数定义中再定义一个函数是非法的。,2作用用来实现某一特定的功能。,charupper(charc)charc1;if(c=a,例如:编写一个函数将小写英文字母转换成大写英文字母。,5.4函数调用,函数的功能是通过函数调用实现的,调用时必须提供与函数定义相对应的各个参数的实际值。1函数调用的格式();说明:函数是“按名调用”的,所以函数名应该准确无误。参数表,给出与函数定义所描述的形式参数相对应的各个实在参数。注意:实参与形参必须类型一致,个数相等,顺序吻合。函数调用时所提供的实参,可以是常量、变量、表达式、函数调用等。但其值必须是确定的。,例如:,例1:sqrt(30)实参是常量;sqrt(a)实参是变量;sqrt(a*100)实参是表达式;sqrt(abs(x)实参是另一函数调用(求x的绝对值);例2:(P:129)sqrt(pow(sin(x),2.5)/实参是函数调用说明:sin函数的函数原型为:doublesin(doublex);,2函数调用可以出现在以下三种场合:(参见P:136),1)出现在表达式中:有返回类型的函数调用可以参加表达式运算,或直接赋值给对应类型的变量,构成表达式语句。如:2*sqrt(56)2)函数调用语句:即函数调用独立构成一条语句。例如:print();strcpy(s1,s2);3)作为函数的参数:函数调用的实参是某一函数调用。如:max(a,max(b,c)sqrt(abs(x),3函数调用机制和调用过程(P:145),运行时的内存布局程序要运行,就要先将可执行程序文件装载到计算机的内存中,其装载工作由操作系统掌控。一般而言,操作系统将程序装入内存后,将形成一个随时可以运行的进程空间,该进程空间分四个区域:(1)代码区:存放程序的执行代码,即程序中各个函数的代码;(2)全局数据区:存放程序中的全局数据和静态数据;(3)堆区:存放程序的动态数据;(4)栈区:存放程序的局部数据。,3函数调用机制和调用过程(P:145),栈区在C+中函数调用的整个过程需要“栈”的参与。“栈”是一种“先进后出”的数据结构。即:先压进去的数据最后弹出。C+在处理函数调用时使用的栈称为运行栈。它用来存放返回地址、函数参数以及函数内部定义的数据等。运行栈随着程序的运行而动态变化。,3函数调用机制和调用过程(P:145),函数调用过程每一个函数的调用过程都包括:调用初始化、执行、善后处理三个阶段,具体分为以下步骤:(1)在运行栈的栈顶建立被调函数的栈空间;(2)保护主调函数的运行状态和返回地址;(3)传递参数;(4)将运行控制权交给被调函数;(5)执行被调函数;(6)取出返回地址;(7)恢复主调函数的运行状态;(8)返回主调函数。,初始化阶段,/执行阶段,善后阶段,/例54(P:146f0503.cpp)栈区运动的演示程序intfuncA(intx,inty);voidfuncB(int,程序的执行过程,(1)main最先执行最后结束,funcB最后执行却最先结束;(2)每一函数在调用另一函数时必须暂时中断自身的执行;(3)每一被调用的函数执行结束后,必须能返回主调函数调用处的下一执行点,也即调用前必须保留返回地址。,函数调用过程中的运行栈P:147,运行栈会随着程序的运行而动态变化。,4.参数传递,参数传递是函数调用过程中十分重要的工作。所谓参数传递是指实在参数与形式参数之间的数据传递。(1)传递顺序:按参数表中参数的排列顺序自左向右逐一传递。(2)传递方式:有三种传递方式(形实结合方式)传值传递指针传递引用,传值(P:137),传值:在函数被调用时,将实参的值存入对应形参的参数区中。实参可以是变量、常量、表达式。特点:被调函数对参数的修改不会影响主调函数用做实参的数据对象。这样的参数传递方式,只能实现数据的单向传递,即:无法由实参带回函数对参数的任何修改。,例如:/swap_V.cpp#includeusingnamespacestd;voidswap(int,int);intmain()intx=5,y=3,z=7;swap(x,y);coutx=x,y=y,z=zendl;swap(y,z);coutx=x,y=y,z=zendl;return1;voidswap(inta,intb)inttemp=a;a=b;b=temp;,输出结果:x=5,y=3,z=7x=5,y=3,z=7,传递指针:即指针作为函数的参数(例如:/swap_P.cpp),#includeusingnamespacestd;voidswap(int*,int*);intmain()inta=3,b=8;couta=a,b=bendl;swap(,运行结果:a=3,b=8afterswappinga=8,b=3,从这个例子中我们可以看出:在程序设计中通过定义函数的参数是指针类型数据可以使函数返回不止一个的值。,上例中:函数调用语句:swap(必须以变量a和b的地址作为实参函数定义的头部为:voidswap(int*x,int*y),传递引用(即:引用作为函数的参数),例如:voidswap(int,一个函数的参数也可定义成引用的形式。引用形参:在函数定义的参数表中,参数名前加上couta=a,b=bendl;swap(a,b);/直接以变量a和b作为实参调用swap函数coutafterswapping.n;couta=a,b=b0时,n!=n*(n-1)!,求n!的问题就转化成了求n*(n-1)!的问题,而求(n-1)!的问题与求n!的解法相同,只是求阶乘的对象的值减去1。(2)当n的值递减至0时,n!=1从而使递归得以结束。按照以上分析,求n!可以用递归的方法解决。,/用递归调用的方法计算n!#includeusingnamespacestd;longfact(int);intmain()longresult;intn;coutn;if(n=0)result=fact(n);coutendln!=resultendl;elsecoutendl输入的数据不正确!endl;return1;,例(续),longfact(intn)if(n=0)/先测试,后递归调用return(1);elsereturn(fact(n-1)*n);fact函数的执行部分,出现自身的直接调用直接递归调用。,调用过程中,提供给fact的实参按:n,n-1,n-2,1,0规律递减;调用的返回值却按:1,1,2*1,3*2*1,(n)*(n-1)*1的规律变化。,注意:被调用函数运行的栈空间独立于调用函数的栈空间,所以与调用函数之间的数据是无关的。函数之间靠参数传递和返回值来联系,函数看作为黑盒。(参见P:163图5-9),4递归的评价,递归的目的是简化程序设计、增强程序的易读性。但是,函数的递归调用明显会增加系统开销。包括系统空间的开销和运行时间的开销:时间上,执行调用与返回的额外工作要占用CPU时间;空间上,随着程序的执行每递归一次,栈内存就多占用一段空间。在非常讲究程序执行效率的场合,应尽量减少使用递归编程技术;相反,在强调程序结构,程序的易读性和可维护性的场合,却鼓励使用递归程序设计技术。,5.消去递归(P:164),大多数递归函数都能用非递归函数来代替。例如:编写一函数用于求n!。longjcn(intn)/非递归方法inti;longjc=1;for(i=1;i=n;i+)jc*=i;returnjc;,.递归函数的应用,例如:编写一个含有递归调用的程序处理hanoi(汉诺)塔问题。(课外练习)hanoi(汉诺)塔问题描述为:有三根针A,B,C。A针上有N个盘子,盘子大小不等,大的在下,小的在上。要求将N个盘子从A针移到C针,在移动过程中可以借助B针,但每次只允许移动一个盘子,并且在移动过程中在三根针上都保持大盘在下,小盘在上。,5.6函数重载(P:165),1什么叫重载对于在不同的类型上作相同的运算且又用同样的符号或名字的情况,称之为重载。例如:“+”运算符可以用来进行整数、浮点数甚至指针数据的加法运算;cin、cout这两个预定义的流类对象也能适应不同数据类型的输入输出。,2什么叫函数重载,例如:对数值数据求绝对值,如果考虑整型和双精度型两种不同类型的数据。则应该有两个求绝对值函数:intabs(int);/求整型数的绝对值doubledabs(double);/求双精度型数的绝对值由于两个函数都是求绝对值的,如果采用同一个函数名,即表示成函数重载,则更符合人们的习惯。也将给使用者带来极大的方便,便于使用,也便于记忆。如:intabs(int);doubleabs(double);,函数重载:多个功能相同,但参数类型或参数个数不同的函数使用同一个函数名,称为函数重载。,3.重载函数的调用,在调用重载函数时,编译器将根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。abs(-9)intabs(int);abs(-100.9)doubleabs(double);注:实际上abs是一个标准库函数,对应的头文件是math.h。,4.注意:,(1)不要将不同功能的函数定义为重载函数,以免出现误解、混淆。(2)实现函数的重载必须满足下列条件之一:参数表中对应的参数类型不同;参数表中参数个数不同;参数表中不同类型参数的次序不同。,几点说明:,1)返回类型不能区分函数;例如:floatadd(int,float);intadd(int,float);在调用时系统无法区分选择对应的函数。2)采用引用参数不能区分函数;例如:voidprint(double);voidprint(double,4)有些派生基本类型的参数虽然可以区分同名函数,但在使用中必须注意;例如:intabs(int);unsignedintabs(unsignedint);调用:abs(3)因为实参3既可以与int型形参匹配,也可以与unsigendint型形参匹配,故这里可能出现二义性。,5.7默认参数的函数,通常,在调用函数时,要为函数的每个形式参数指定对应的实在参数。例如:voiddelay(inta);/函数声明voiddelay(inta)/函数定义intsum=0;for(inti=1;i=a;+i)for(intj=1;j1000;+j)for(intk=1;k100000;+k)sum+;/这是一个产生时延的函数调用:delay(2);/与a对应的实参为2,1.默认参数的声明,C+可以在函数声明中为参数指定默认参数值。这样,在函数调用时,对应的实参就可以省略。例如:如果将delay()函数中的参数a定义成默认值为2,只需简单地把函数声明改为:voiddelay(inta=2);/函数声明调用:delay();/a采用默认值2delay(5);/a设置为5由此可见,这种形式的函数在调用时具有灵活性。注意:当既有声明又有定义时,默认参数值只能置于声明中。,2.默认参数的顺序规定,如果一个函数中有多个默认参数,则在形式参数分布中,默认参数应从右至左逐个定义。即:函数参数默认值只能从后往前设置。例如:voidfunc(inta=1,intb,intc=3,intd=4);/error!voidfunc(inta,intb=2,intc=3,intd=4);/right!对于第二个函数声明,其调用:func(10,15,20,30);/调用时给出所有实参正确!func(12,12);/参数c和d默认func();/参数a没有默认值错误!func(2,15,20);/只能从右到左顺序匹配默认,3.默认参数与函数重载,默认参数可将一系列简单的重载函数合成为一个。在程序设计中,如果几个重载函数做基本相同的事,只不过参数个数不同而已,可用默认参数值的方法将一系列简单的重载函数合成为一个带默认参数值的函数例如:voidpoint(int,int)/voidpoint(inta)returnpoint(a,4);voidpoint()returnpoint(3,4);可用下面的默认参数的函数来替代:voidpoint(inta=3,intb=4);,如果一组重载函数(可能带有默认参数)都允许相同实参个数的调用,将会引起调用的二义性。例如:voidfunc(int);voidfunc(int,intb=4);调用:func(7);/错误!func(20,30);,5.9指针与函数,1.传递数组的指针性质(P:139)无论何时,将数组作为参数传给函数,实际上只是把数组的地址传给函数。一旦把数组作为参数传递到函数中,则在栈上定义了指针,可以对该指针进行递增、递减操作。例如:arr_point.cpp,#includeusingnamespacestd;voidSum(int,int);intmain()inta10=1,2,3,4,5,6,7,8,9,10;Sum(a,10);return1;voidSum(intarray,intn)intsum=0;for(inti=0;in;i+)sum+=*array;array+;/是一个指针,可以作为左值coutsumendl;,等价于:voidSum(int*,int);voidSum(int*array,intn),注意:由于形参array是指针变量而不是数组,所以不能用sizeof(array)/sizeof(*array)来求取数组元素的个数。,2.使用指针修改函数参数,在程序设计中通过定义函数的参数是指针类型数据可以使函数返回不止一个的值。#includeusingnamespacestd;voidswap(int*,int*);intmain()inta=3,b=8;couta=a,b=bendl;swap(,运行结果:a=3,b=8afterswappinga=8,b=3,3.指针函数,返回指针的函数称为指针函数。指针函数的定义形式:类型标识符*函数名()例如:point_func.cpp定义findmax()函数,其功能是寻找数组中的最大元素,将该元素的下标通过参数返回,并返回其地址值,编程实现findmax()函数。int*findmax(int*array,intsize,int*index);,#includeusingnamespacestd;int*findmax(int*array,intsize,int*index);intmain()inta10=33,91,54,67,82,37,85,63,19,68;int*maxaddr;intidx;maxaddr=findmax(a,sizeof(a)/sizeof(*a),4.函数指针(P:150),在程序运行中,全局变量存放在全局数据区(dataarea),局部变量存放在栈区(stackarea),动态变量存放在堆区(heaparea)。函数则存放在代码区(codearea)。函数和变量有两点相通之处:(1)有类型(返回类型),(2)有地址,称为入口地址。函数的地址也可作为指针的值,这就是函数指针。指向函数入口地址的指针称为函数指针。通过函数指针可以调用相应的函数。,(1)定义函数指针,定义一个函数指针要说明函数的返回值类型、函数参数列表。一般语法如下:数据类型(*函数指针名)(形参表)说明:1)数据类型指明函数指针所指函数的返回值类型;2)第一个圆括号中的内容指明一个函数指针的名称;3)形参表则列出了该指针所指函数的形参类型和个数。例如:int(*func)(chara,charb);区别int*func(chara,charb);注意:函数指针与指针函数的区别,(2)对函数指针赋值,函数指针在使用之前也要进行赋值,使指针指向一个已经存在的函数代码的起始地址。一般语法如下:函数指针名函数名;注意:(1)赋值号右边的函数必须是一个已经定义过的,和函数指针所指向的函数一致的函数。(2)省略方括号的数组名是地址;省略圆括号的函数名也是地址。,例如:,intfn1(charx,chary);int*fn2(charx,chary);intfn3(inta);int(*fp1)(chara,charb);int(*fp2)(ints);fp1=fn1;fp1=fn2;/Error!fp2=fn3;fp2=fp1;/Error!fp2=fn3(5);/Error!,注意:,(1)一个函数不能赋给一个不一致的函数指针;两个函数指针的相互赋值同样也要求函数一致。(2)函数指针与其他类型的指针尽管都是指针,但在类型上有很大差别。两类指针之间不允许互相赋值,甚至显式转换也不行。,(3)通过函数指针来调用函数,在赋值之后,可以通过函数指针名来直接调用指针指向的函数。例如:intfn3(inta);int(*fp2)(ints);/定义一个函数指针fp2=fn3;调用:fp2(5);/常用,ANSIC+标准等价fn3(5);,(4)用typedef来简化指针,要定义同一类型的函数指针,最好将函数指针类型用别名声明。例如:typedefint(*FUN)(inta,intb);/声明FUN是一函数指针类型注意:FUN不是函数指针名,只是一个函数指针类型名。FUNfunp;/定义funp为一函数指针,(5)函数指针用作函数参数,函数指针主要用来进行参数传递。例1:func_point_arg.cpp#include#includeusingnamespacestd;doublesigma(double(*func)(double),doubledl,doubledu)doubledt=0.0;for(doubled=dl;ddu;d+=0.1)dt+=func(d);/用函数指针调用函数returndt;,函数指针,intmain()doubledsum;dsum=sigma(sin,0.1,1.0);coutthesumofsinfrom0.1to1.0isdsumendl;dsum=sigma(cos,0.5,3.0);coutthesumofcosfrom0.5to3.0isdsumendl;return1;注:标准库函数sin()、cos()的原型为:doublesin(doublex);/表示x弧度的正弦值doublecos(doublex);/表示x弧度的余弦值,(6)函数指针数组,函数指针可构成指针数组例如:在程序设计中,利用函数指针数组,我们即可根据用户的不同选择方便地调用不同的函数完成相应的任务。/f0507.cpp函数指针数组#includeusingnamespacestd;/-typedefvoid(*MenuFun)();/声明函数指针类型voidf1()coutgood!n;voidf2()coutbetter!n;voidf3()coutchoice;switch(choice)case1:fun0();break;case2:fun1();break;case3:fun2();break;case0:return0;default:coutyouenteredawrongkey.n;return1;,5.10向量与函数(P:87),一、向量数组的局限性在声明时数组的大小是固定的,并且在程序运行时不能改变。以数组作为函数参数时,必须同时提供数组的大小作为参数。C+数组间不允许相互赋值。向量(vector)向量是一个基于模板的类,它存储着相同数据类型的元素。vector是C+STL(标准模板类库)的重要一员,使用时必须包含头文件vector。向量的模板形式:vectortype为元素类型名,它可以是任何合法的数据类型。,vector的定义,vectors;/创建一空向量vectora(10);/创建一包含10个整型元素的向量vectorb(10,1);/创建一包含10个整型元素的向量,并把每个元素值初始化为1vectorc(b);/用另一个已经存在的向量b,来创建一个新向量cvectord(b.begin(),b.begin()+3);/用b的前个元素来初始化新创建的向量dintintArray5=9,2,7,3,12;vectorintVector(intArray,intArray+5);/用数组来初始化向量的值,向量中的遍历器类型为:vector:iterator输出向量中的所有元素:两种方法(vector_out.cpp),vectora(10,3);方法一:遍历器方式for(vector:iteratorit=a.begin();it!=a.end();+it)cout*it“”;/指针间访形式方法二:下标方式for(inti=0;ia.size();+i)coutai,;,向量常用操作,size():返回向量中的元素个数resize():修改向量的元素个数:多则删,少则增front():返回向量中第一个元素的值back():返回向量中最后一个元素的值clear():清空向量(不再有元素)empty():如果向量为空则返回true,否则返回falsepush_back():在向量的末尾添加一个元素pop_back():删除向量末尾的一个元素begin():Returnsaniteratorpointingtothebeginningofthevectorend():Returnsaniteratorpointingtotheendofthevector例如:(P:88),例如:(P:88)vector_op.cpp,a.assign(b.begin(),b.begin()+3);/b向量的前3个元素构成向量赋给aa.assign(4,2);/a向量含4个元素,全初始化为2intx=a.back();/a的最后一个元素赋给变量xinty=a.front();/a的第一个元素赋给变量ya.pop_back();/删除a的最后一个元素a.push_back(5);/在a最后插入一个元素,其值为5a.resize(10);/a元素个数调至10。多删少补,其值随机a.resize(10,2);/a元素个数调至10。多删少补,新添元素初值为2if(a=b)cout”equal”;/a与b的向量比较操作a.clear();/a向量清空(不再有元素)if(a.empty()couta;)s.push_back(a);/在s向量最后插入一个元素aintpair=0;for(inti=0;is.size()-1;+i)for(intj=i+1;js.size();+j)if(si=sj)pair+;coutpairMat;/定义二维向量的名称为MatMatinput();voidmySort(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 汽修一级考试题库及答案
- 中医病因考试题目及答案
- 2025年广州中小学教师心理健康B证班结业考试题目及答案
- 检验技术员考试题及答案
- 科学数学考试卷子及答案
- 中国现代史考试题及答案
- 农民专业合作社与土地承包合同
- 规范税收缴纳承诺书8篇范文
- 合同管理标准化文件模板汇编
- 人员面试笔试题库及答案
- 2025至2030中国石油化工设备行业发展分析及发展趋势分析与未来投资战略咨询研究报告
- 护理病历讨论制度
- 电子病历系统集成与建设方案
- 新生儿个体化发育支持护理
- 电子工业出版社小学信息技术五年级上册全册教案(全册)
- CJ/T 526-2018软土固化剂
- (高清版)DG∕TJ 08-2251-2018 消防设施物联网系统技术标准
- 冻伤的处理与急救措施
- 装修公司草签合同协议
- 《解剖学课件:人体解剖学概要》
- 粮食代烘干合同协议
评论
0/150
提交评论