版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第五章成员函数第一页,共八十四页,2022年,8月28日5.1成员函数的概念
为了实现对象的行为,我们把一些相关的语句组织在一起,并给它们注明相应的名称,形成一些相对独立而且便于管理和阅读的小块程序,每个小程序块描述了一个完整的行为,这种形式的组合就构成了成员函数。面向对象程序设计第二版第二页,共八十四页,2022年,8月28日5.2算法描述
5.2.1算法的概念
一个完整的对象应该包括两个方面的内容,即对数据的描述(对象属性)和对操作的描述(对象行为)。对操作的描述也就是算法,是设计和实现对象行为的灵魂。一个完整的算法应该具有以下5个特性:有穷性、确定性、有效性、输入性和输出性。
面向对象程序设计第二版第三页,共八十四页,2022年,8月28日5.2.2算法的表示
为了表示一个算法,可以采用多种形式。最常见的几种方法有自然语言表示法、流程图表示法、伪代码表示法和计算机语言表示法。自然语言表示法:自然语言表示法就是用我们日常生活中的语言来表示算法的实现过程,对采用何种语言没有限制。但是采用自然语言表示往往不太严格,容易造成歧义。另外,用这种方法描述包含分支和循环的算法不太方便。因此,自然语言表示法很少用来描述复杂的算法。面向对象程序设计第二版第四页,共八十四页,2022年,8月28日面向对象程序设计第二版图5.1一般流程图表示算法流程图表示法:流程图就是用一些图框表示各种操作。用流程图来表示算法最大的优点就是直观形象,便于理解,并且实用性强,能够方便的表示包含分支和循环的结构,避免了用自然语言表示算法的不足。第五页,共八十四页,2022年,8月28日面向对象程序设计第二版图5.2N-S流程图表示算法第六页,共八十四页,2022年,8月28日伪代码表示法:伪代码是用介于自然语言和计算机语言之间的文字和符号来描述算法。从结构上看,伪代码表示法类似于最终的计算机语言,每一行就表示一个基本的操作。它不用图形符号,因此书写方便、格式紧凑,也易于理解,便于向最终的程序过渡。伪代码表示法书写格式比较自由,容易直观表达出设计者的思想。同时,书写的算法也容易修改,很容易写出结构化的算法。因此,这种方法一般被专业的软件开发人员采用,特别是在详细设计中采用伪代码表示法,可以使以后的编码阶段变得简单。面向对象程序设计第二版第七页,共八十四页,2022年,8月28日计算机语言表示法: 一个算法要拿到计算机上去运行,最终还是得采用计算机语言表示。计算机是无法识别流程图和伪代码的。只有严格按照计算机语言语法编写的程序才能被计算机执行。目前使用的计算机语言多种多样,本书所介绍的面向对象的程序设计语言(C++)就是种很好的描述和实现算法的工具。面向对象程序设计第二版第八页,共八十四页,2022年,8月28日5.3成员函数的定义与调用
5.3.1成员函数的定义面向对象程序设计第二版定义一个成员函数必须具备四个条件:⑴函数必须具有返回值类型,即函数必须有返回值,且该返回值的类型应该与函数返回值的类型一致⑵函数必须具有一个名字,即函数名。⑶函数必须具有一个形参(形式参数)列表,C++规定,形参列表以左括号“(”开始,到右括号“)”结束,且各个形参之间用“,”分开。⑷函数要完成一定的功能,必须具有一个函数体,其中,前面的三个条件构成了函数的原型。第九页,共八十四页,2022年,8月28日说明一个函数原型的一般形式有两种:函数返回值类型函数名(参数类型1参数名1,参数类型2参数名2,…);函数返回值类型函数名(参数类型1,参数类型2,…); 以上两种方法均可以使用,但是为了增加程序的可读性建议使用第一种方法。面向对象程序设计第二版第十页,共八十四页,2022年,8月28日成员函数的定义:(1)对于代码较少的成员函数的定义,可以直接在类中进行。
classCircle{protected:
intRadius;
…
public:
fioatcircle_area(fioatpi){returnpi*Radius*Radius;}…};(2)对于代码较多的成员函数的定义,通常只在类中进行原型说明,在类外对成员函数进行定义。
classCircle{protected:
intRadius;
…
public:
fioatcircle_area(fioatpi);
…};floatCircle::circle_area(floatpi){returnpi*Radius*Radius;}请注意,这时的函数名应该包含:类名(Circle)+作用域分辨符(::)+原函数(circle_area)第十一页,共八十四页,2022年,8月28日5.3.2成员函数的调用一般形式有两种:
对象名.函数名(实参1,实参2,…); 对象指针→函数名(实参1,实参2,…);例如:voidmain(){ ┇ cout<<“Theareaofcircle=”<<mycircle.circle_area(3.14); ┇}面向对象程序设计第二版第十二页,共八十四页,2022年,8月28日关于形参和实参的说明:
⑴在函数定义时说明的形参,并不占内存中的存储单元。只有在函数调用时,形参才被分配内存单元,并被赋以实参的值。在调用结束后,该部分内存立刻被释放。⑵实参可以是常量、变量、或表达式等,但要求它们必须有确定的值。面向对象程序设计第二版第十三页,共八十四页,2022年,8月28日5.3.3.1函数指针在程序运行中,每个函数在编译时都会被编译器分配给一个入口地址,则指向这个入口地址的指针就称为函数指针。函数代码是程序的算法指令部分,被存放在代码区(code),函数指针指向代码区中某个函数的地址,因此,通过函数指针可以调用相应的函数。面向对象程序设计第二版第十四页,共八十四页,2022年,8月28日⑴函数指针的声明正确的指针类型声明如下: 返回值类型(*函数指针名)(参数表); 例如:int(*func_p)(chara,charb);其中,“int”表示函数的返回值类型为整型,“(*func_p)”表示func_p是一个指针,后面的(chara,charb)表示该指针(func_p)是函数指针。其定义后产生的指针变量有全局、静态与局部之分,同样占有内存空间(与其他类型指针变量所占空间相同)。面向对象程序设计第二版第十五页,共八十四页,2022年,8月28日由于(*func_p)所扮演的角色与函数名相同,因此在使用(*func_p)时,只需将他看作函数名即可。请看下面一个例子,学习如何通过函数指针调用函数: intfunc(int); int(*func_p)(int); func_p=func; //↑函数指针(*func_p)指向代码区函数func()的地址 intx=func(4);//使用函数func(),这个用法很常见 inty=(*func_p)(5); //↑使用函数指针(*func_p)实际上调用了func()函数面向对象程序设计第二版第十六页,共八十四页,2022年,8月28日⑵函数指针可以作为函数的参数,如下所示:编写一个函数,计算一个字符串的长度intlen(constchar*string) { constchar*p=string; for(inti=0;*p!=‘\0’;i++) { p++; } returni;}面向对象程序设计第二版第十七页,共八十四页,2022年,8月28日通过函数指针调用上述函数intCaller(constchar*string,int(*f_p)(constchar*))//↑通过形参的形式,说明一个函数指针int(*f_p)(constchar*){ return(*f_p)(string); //通过参数结合的形式将函数 //指针指向被调用的函数intlen(constchar*string) }面向对象程序设计第二版第十八页,共八十四页,2022年,8月28日注意:(1)定义一个函数指针与定义一个返回值是指针的函数不同 int(*f_p)(constchar*);//定义一个函数的指针 int*func_p(chara){…}//定义一个返回指针的函数(2)可以用typedef简化函数指针的使用,如下例: typedefint(*FUN)(inta,intb); //声明(*FUN)是一个函数指针类型 FUNfunc_p; //func_p为一个返回整型和两个整型参数的函数指针(3)函数指针可以和其他指针一样地使用面向对象程序设计第二版第十九页,共八十四页,2022年,8月28日回调函数 简单来说,回调函数就是指通过函数指针调用的函数。当你把函数指针作为参数传递给另一个函数,另一个函数通过该指针调用该函数,我们就称被调用的这个函数为一个回调函数。如下所示:
voidCallBack(chara){…} //准备作为回调函数的函数 int(*func_p)(chara);//指向回调函数的指针 func_p=CallBack; //将函数指针指向回调函数 voidfun(intx,int(*f_p)(chara)){(*f_p)(“hello”);…} //准备使用回调函数的函数 fun(5,func_p); //使用回调函数,我们通过函数指针 //隐式的调用了回调函数void //CallBack(chara)面向对象程序设计第二版第二十页,共八十四页,2022年,8月28日
回调函数可以消除调用者和被调用者之间的耦合性。调用者不用关心需要调用哪个函数,它所需要知道的仅仅是函数原型。让我们通过一个例子来体会回调函数的威力。假设我们开发了若干个函数用来求两数之和,这些函数针对不同的数据类型:
voidSumForInt(int*oper1,int*oper2,int*result){ *result=*oper1+*oper2;}voidSumForFloat(float*oper1,float*oper2,float*result){ *result=*oper1+*oper2;}voidSumForDouble(double*oper1,double*oper2,double*result){ *result=*oper1+*oper2;}面向对象程序设计第二版第二十一页,共八十四页,2022年,8月28日
但是这些函数名各不相同,无法向使用者提供一个统一的接口,为了达到这个目的,我们可以采用回调函数机制:voidSum(void*operand1,void*operand2,void*result,void(*pSum)(void*,void*,void*)) //这里pSum就是一个指向回调函数的指针。 //定义统一接口:voidSum(void*operand1,void*operand2,void*result,void(*pSum)(void*,void*,void*)){ (*pSum)(operand1,operand2,result);}面向对象程序设计第二版第二十二页,共八十四页,2022年,8月28日下面一段代码向读者介绍如何使用这个接口:voidmain(){ intiOper1=1; intiOper2=2; intiResult; floatfOper1=1.3f; floatfOper2=2.6f; floatfResult; doubledOper1=1.5; doubledOper2=2.8; doubledResult; Sum(&iOper1,&iOper2,&iResult,SumForInt); //两个整数相加 Sum(&fOper1,&fOper2,&fResult,SumForFloat); //两个单精度数相加 Sum(&dOper1,&dOper2,&dResult,SumForDouble);//两个双精度数相加}面向对象程序设计第二版第二十三页,共八十四页,2022年,8月28日
在实际项目开发过程中回调函数应用非常广泛,如windows的窗体消息处理函数就使用了回调函数机制,当注册窗体类时,需要传入该窗体的消息处理函数指针,这样窗体的创建是由系统完成,而窗体消息的处理,则由开发人员定制,从而为窗体的创建和处理解耦。第二十四页,共八十四页,2022年,8月28日注意:⑴在面向对象的程序设计过程中,通常将回调函数定义为类的静态成员函数。这是因为类的非静态成员函数指针代表的是在某个实例中该函数相对于实例首址的偏移量,而不是它在内存中的绝对地址,因此不能脱离实例单独作为回调函数指针来使用。⑵有的程序员直接将全局函数作为回调函数。此种做法打破了面向对象的编程风格,因此不建议使用。⑶回调函数的使用是滞后联编的,主要取决于在调用时函数指针指向了哪个函数,因此也可以说,回调函数机制是面向对象程序设计中多态性的另一种体现。
面向对象程序设计第二版第二十五页,共八十四页,2022年,8月28日5.3.4内联函数内联函数的引入较好地解决了时间开销的问题。这是因为在程序编译时,编译器将程序中调用内联函数的表达式,用内联函数的函数体来代替。但是,由于编译时用函数体替代了调用表达式,因而增加了目标程序的代码量,带来了空间开销的增加。定义内联函数的一般格式如下:inlineintf_add(intx,inty){ returnx+y;}其中,inline是关键字用来说明f_add()是一个内联函数。面向对象程序设计第二版第二十六页,共八十四页,2022年,8月28日请看下例:intmain(){ f_add(2,3); return0;}注意:(1)内联函数体内不允许使用循环语句和开关语句(这两种语句的介绍见5.4节);(2)内联函数的定义必须出现在内联函数第一次调用之前;(3)所有在类定义体内部定义的函数都是内联函数(虚函数除外);面向对象程序设计第二版第二十七页,共八十四页,2022年,8月28日5.4运算符和表达式
5.4.1运算符及运算符优先级像代数中一样,C++中的运算符是连接参加运算的数据的符号,不同的运算符规定了不同的运算方法和不同的运算规则(如先乘除后加减)C++语言的运算符,基本上可以分为以下几种类型。见下表5.1所示。其中,结合性是指:当一个表达式由相同优先级的运算符构成时,其运算符的优先次序问题。L表示从左到右地依次进行,R表示由右向左依次进行。第二十八页,共八十四页,2022年,8月28日优先顺序类别符号举例功能结合性1作用域运算符::::ex_x全局范围(ex_x是全局变量)R::Myclass::count类范围(访问Myclass类中的静态成员)L2成员存取运算符→Pa→xPa是指针对象,x是成员L·a·xa是非指针对象,x是成员数组下标运算符[]X=A[5]将数组A中下标为5的元素赋给XL函数调用运算符()X=f()将函数f()的返回值赋给XL3Sizeof运算符sizeofsizeof(X)获得数据类型X的长度R增量与减量运算符++++X使用X之前,先使X的值加1R++X++使用X之后,再使X的值加1R----X使用X之前,先使X的值减1R--X--使用X之后,再使X的值减1R逻辑非运算符!!(X>Z)将!右边的结果(逻辑值)取非R按位取反运算符~~A将A值按位取反R取地址与取值运算符&pX=&Y提取变量Y的地址,然后赋给指针变量pXR*X=*pY提取指针变量pY所指的地址单元的内容,然后赋给变量X类型转换运算符(类型)X=(int)Y将Y转换为整型,然后赋给XR动态内存分配运算符NewPa=newint[4];创建一个整型数组,其中含有4个元素,并将首地址给PaRdeletedelete[]Pa;deletePb;数组必须用delete[]进行删除普通的堆对象可用delete删除面向对象程序设计第二版第二十九页,共八十四页,2022年,8月28日4-*X*Y将两数相乘L/X/Y将两数相除%X%Y将两数相除取其余数5+X+Y将两数相加X–Y将两数相减6移位运算符<<A<<2将A值向左平移2位L>>A>>2将A值向右平移2位7!=<X<Y判断X是否小于YL>X>Y判断X是否大于Y<=X<=Y判断X是否小于等于Y>=X>=Y判断X是否大于等于Y8==X==Y判断X是否等于YX!=Y判断X是否不等于Y9位运算符&A&B将A和B的值按位相与L10^A^B将A和B的值按位相异或11|A|B将A和B的值按位相或12逻辑运算符&&X<Y&&X>Z将&&两边的结果(逻辑值)相与L13||X<Y||X>Z将||两边的结果(逻辑值)相或14条件运算符?:Z=X>Y?E1:E2;如果X>Y为真,Z等于E1的值,否则等于E2的值R面向对象程序设计第二版第三十页,共八十四页,2022年,8月28日15赋值运算符=X=Y将Y的值赋给XR+=X+=Y将X的值加Y后,再赋给X,等价于X=X+Y-=X-=Y将X的值减Y后,再赋给X,等价于X=X–Y*=X*=Y将X的值乘Y后,再赋给X,等价于X=X*Y/=X/=Y将X的值除Y后,再赋给X,等价于X=X/Y%=X%=Y将X的值对Y取余后,再赋给X,等价于X=X%Y&=X&=Y将X的值与Y按位相与后,再赋给X,等价于X=X&Y^=X^=Y将X的值与Y按位相异或后,再赋给X,等价于X=X^Y|=X|=Y将X的值与Y按位相或后,再赋给X,等价于X=X|Y<<=X<<=Y将X的值左移Y位后,再赋给X,等价于X=X<<Y>>=X>>=Y将X的值右移Y位后,再赋给X,等价于X=X>>Y16逗号运算符,E1,E2,E3从左到右依次求出表达式E1,E2,E3的值L面向对象程序设计第二版第三十一页,共八十四页,2022年,8月28日5.4.2表达式所谓表达式是指:用运算符将运算对象(也称操作数)连接起来的、符合语法规则的式子。根据连接运算对象的连接符(运算符)的不同大体上可以将表达式分为以下四种:⒈算术运算表达式(算术运算表达式的运算结果是数值)⒉逻辑运算表达式(逻辑运算表达式的运算结果是逻辑值)⒊赋值运算表达式(赋值运算符的结合性是从右向左的)⒋逗号运算表达式(逗号运算表达式的值是最后一个表达式的值)面向对象程序设计第二版第三十二页,共八十四页,2022年,8月28日注意:
⑴当一个表达式中包含多种运算符时,必须考虑运算符的优先级和结合性。⑵可以用()改变运算符的优先级;例如: inti=1,n=2; n=i+n<<1;//因为“+”高于“<<”高于“=”,所以先运算i+n等于3, //再运算3<<1得6,最后n=6。用“()”改变先后次序如下: inti=1,n=2; n=i+(n<<1);//因为(n<<1)高于“+”高于“=”,所以先运算n<<1等 //于4,再将4+1得5,最后n=5。面向对象程序设计第二版第三十三页,共八十四页,2022年,8月28日⑶只要符合表达式定义的式子,都是表达式,不一定非要包含赋值运算符,如下面的式子都是正确的C++表达式: 3+5; a,b,c; x>y?e1:e2; i++;⑷赋值运算符左侧必须是一个可以引用的存储单元(如:变量),决不能是表达式。面向对象程序设计第二版第三十四页,共八十四页,2022年,8月28日5.4.3数据类型的转换类型转换就是将一种类型的值转换为另一种类型的值。⒈隐式类型转换其转换过程是由系统按照一定的转换规则自动完成的。(1)当char或short型数据与int型数据进行运算时,将char或short型装换成int类型;(2)当两个运算对象数据类型不一致时,在做算术运算前,级别低的自动转换为级别高的数据类型(3)在赋值表达式E1=E2的情况下,赋值运算符右端E2的结果值需转换为E1类型,然后赋值。面向对象程序设计第二版第三十五页,共八十四页,2022年,8月28日⒉显式类型转换显式类型转换的方法有两种。(1)强制转换法强制转换法的格式为:(类型名)表达式;如:(int)x;在强制类型转换时,得到一个所需类型的中间变量,原来变量的类型未发生变化。(2)函数法函数法的转换格式为:类型名(表达式);如:int(x);第三十六页,共八十四页,2022年,8月28日显示类型转换有时是必需的,如前面提到的void*指针。我们必须将malloc返回的void*指针显示转换为特定的类型
int*p=static_cast<int*>(malloc(100));(int)3.1415;//就是取整数部分(int)a%10;//取a的个位数第三十七页,共八十四页,2022年,8月28日在程序设计过程中,使用以下三种控制结构作为算法的基本单元。事实上,任何一个成员函数的定义都离不开这三种基本结构:
5.5.1顺序结构
顺序结构是指程序语句按顺序,自上而下依次执行
5.5.2选择结构(又称分支结构)
选择结构是指能根据选择条件,改变程序走向的一种语句
5.5.3循环结构
循环结构是指能根据循环条件,重复执行某段程序的一种语句。是代码可重用技术的一种基本形式
5.5控制结构第三十八页,共八十四页,2022年,8月28日5.5.1顺序结构顺序结构是指程序语句按顺序,自上而下依次执行。如,输入三角形的三边长,求三角形面积的函数。floatf_area(floata,floatb,floatc){ floats; s=1.0/2*(a+b+c); returnsqrt(s*(s–a)*(s-b)*(s-c));}面向对象程序设计第二版第三十九页,共八十四页,2022年,8月28日5.5.2选择结构(又称分支结构)⒈if和if…else⑴if是最简单的选择语句,一般格式为: if(<条件表达式>)<语句>我们所要执行的语句多于一条时,C++规定,必须将这些语句用“{”和“}”括起来。一般格式为:if(<条件表达式>){ <语句1> <语句2> … <语句n>} <语句n+1>面向对象程序设计第二版第四十页,共八十四页,2022年,8月28日⑵if…else是用来解决在互相对立的两种条件下,所要分别进行处理的两批语句。
if(<条件表达式>){ <语句1> <语句2> … <语句n>}else{ <语句n+1> <语句n+2> … <语句n+k>}<语句n+k+1>面向对象程序设计第二版第四十一页,共八十四页,2022年,8月28日⑶当分支结构(即选择结构)的分支较多时,C++提供了嵌套式的选择结构:if(<条件表达式1>){ <语句1> <语句2> … <语句n>}elseif(<条件表达式2>){ <语句n+1> <语句n+2> … <语句n+k>}else{ <语句n+k+1> <语句n+k+2> … <语句n+k+m>}<语句n+k+m+1>面向对象程序设计第二版第四十二页,共八十四页,2022年,8月28日
例如,设计一个函数,当输入大于0时,输出为1;当输入小于0时,输出为-1;当输入等于0时,输出为0;(注意本例中判断条件有三种情况)intf_s(intx){ if(x<0) { x=-1; }elseif(x>0) //到此排除了x<0的情况,剩下x>0和x=0的情况 { x=1; }else //到此排除了x<0的情况和x>0的情况,只剩下x=0的情况 { x=0; } returnx;}注意:判断条件之间存在排除关系的这种情况,一般采用if…elseif…else这种形式进行处理面向对象程序设计第二版第四十三页,共八十四页,2022年,8月28日
switch语句又称开关语句。主要用于根据某个整型数据的变化,达到多种分支的目的。一般格式为:
switch(整型表达式E){ case整型表达式E1: 语句系列1; break; //跳过下面的所有语句直接去执行语句k+2; case整型表达式E2: 语句系列2; break; … case整型表达式Ek: 语句系列k; break; default: 语句系列k+1;} //开关语句结束 语句k+2; …面向对象程序设计第二版第四十四页,共八十四页,2022年,8月28日注意:
整型表达式E,E1,E2,…,Ek的值,是整型数据(或者字符变量、字符常量)。当整型表达式E的值等于整型表达式E1的值时,执行语句系列1;同理当整型表达式E的值等于整型表达式Ek的值时,执行语句系列k。当程序执行到break时,将跳过下面所有的语句,执行开关语句后的语句,即语句k+2。当整型表达式E的值不等于整型表达式E1,E2,…,Ek的值时,则执行default语句下的语句系列k+1。面向对象程序设计第二版第四十五页,共八十四页,2022年,8月28日例如,编程实现两个浮点数的四则运算函数f_arith:floatf_arith(floatd1,floatd2,charop){ floattemp; switch(op) { case‘+’: temp=d1+d2; break; //跳过下面的所有语句直接去执行switch语句后面的语句 case‘-’: temp=d1-d2; break; case‘*’: temp=d1*d2; break; case‘/’: temp=d1/d2; break; default: temp=-100; } //开关语句结束 returntemp;} //函数结束面向对象程序设计第二版第四十六页,共八十四页,2022年,8月28日5.5.3循环结构⒈for语句⑴当参加循环的语句(又称循环体),只有一条语句时,其一般格式为:for(E1;E2;E3) <循环体语句1>例如:计算从1到n的自然数的累加和函数f_sum。intf_sum(intn){ inti,sum=0; for(i=1;i<n+1;i++) { sum+=i; } returnsum;}面向对象程序设计第二版第四十七页,共八十四页,2022年,8月28日⑵当参加循环的语句,多于一条语句时,需要用“{”和“}”将循环体括起来。其一般格式为:for(E1;E2;E3){ <循环体语句1> <循环体语句2> … <循环体语句n>}面向对象程序设计第二版第四十八页,共八十四页,2022年,8月28日⑶break语句与continue语句对循环的影响break语句:
当某个条件满足,我们想结束循环时,采用这个语句。其一般形式为:for(…){ <语句系列> if(条件表达式E) break; //离开整个循环直接跳到循环体外,执行<语句n+1> ….}<语句n+1>面向对象程序设计第二版第四十九页,共八十四页,2022年,8月28日continue语句: 当某个条件满足,我们并不想结束循环,只想跳过某些循环语句时,采用这个语句。其一般形式为:for(…) //循环起点{ <语句系列1> if(条件表达式E) continue; //跳过<语句系列2>,重返循环起点 <语句系列2>} //循环终点<语句n+1>面向对象程序设计第二版第五十页,共八十四页,2022年,8月28日计算[n,n+k]范围内的自然数中偶数的累加和(其中n和k均为自然数)intf_psum(intn,intk){ intsum=0; while(n>0) //确保n是自然数才计算 { if(k<0) //结束计算的条件,即跳出循环 { break; } if((n+k--)%2!=0)//奇数不累加,即跳过下面的语句重返循环起点 { continue; } sum+=n+k+1; } returnsum;}面向对象程序设计第二版第五十一页,共八十四页,2022年,8月28日⑷多重循环。在某个循环语句内还包含另一个循环语句。C++中称为多重循环(或循环嵌套)。其一般格式为:for(E1;E2;E3) //外循环语句起点{ <外循环体语句1> //外循环体语句系列 <外循环体语句2> for(E11;E22;E33) //内循环语句起点 { <内循环体语句1> //内循环体语句系列<内循环体语句2> … <内循环体语句n> } //内循环语句终点 …<外循环体语句n>} //外循环语句终点面向对象程序设计第二版第五十二页,共八十四页,2022年,8月28日⒉while语句一般格式为:while(<条件表达式>){ <循环体语句系列>}⒊do…while语句一般格式为:do{ <语句系列> }while(<条件表达式>);面向对象程序设计第二版第五十三页,共八十四页,2022年,8月28日注意:
循环语句的形式比较多,要根据需要合理选用。while和do…while型循环语句,同样可以实现多层嵌。break和continue语句均可使用于while和do…while语句,但是,它们仅作用于本层循环上,在任何一层上要实现break和continue的操作,都必须在这一层中增加这两条语句。在书写时,不要忘记do…while语句中最后的“;”。建议读者养成良好编程习惯,不要在循环体内说明数据成员或者函数的原型,如:intnum;或者char*func(inta,char*s);类似的声明应在循环体外。面向对象程序设计第二版第五十四页,共八十四页,2022年,8月28日5.5.4递归——函数的自我调用前面介绍了控制结构,下面我们通过递归的学习进一步加强对控制结构的理解。所谓“递归”是指通过重复执行相同的计算来解决问题。简单的说,递归就是函数直接或间接地自己调用自己。从而使得程序简短。编写递归程序的关键是:(1)构造递归表达式。将n阶的问题转化为比n阶小的问题,转化以后的问题与原来的问题的解法是相同的。(2)必须寻找一个明确的递归结束条件,称为递归出口。 递归是在函数中出现调用函数本身的语句,只是每次调用时函数的实参值不一样,如果代码中没有包含终止调用的控制语句,将无限循环下去。因此,通常用if语句中条件的判断来决定是否跳出这个递归过程。面向对象程序设计第二版第五十五页,共八十四页,2022年,8月28日#include<stdafx.h>#include<iostream.h>intf(intx){ inty; if(x==0||x==1){return3;} y=x-f(x-2); returny;}voidmain(){ cout<<“f(5)=”<<f(5)<<endl; cout<<“f(6)=”<<f(6)<<endl; cout<<“f(8)=”<<f(8)<<endl;}运行结果为:f(5)=5f(6)=1f(8)=7面向对象程序设计第二版下面请同学们看一个例子:第五十六页,共八十四页,2022年,8月28日注意:⒈使用递归编写程序虽然代码较少,但程序的可读性较差。⒉使用递归时层次不能太深,一般不超过3层。多层函数递归将造成系统资源的严重浪费。⒊由于递归的缺点很明显,所以在实际开发工作中不建议使用递归。面向对象程序设计第二版第五十七页,共八十四页,2022年,8月28日5.6函数的参数的传递机制
当一个函数定义有形参时,在进行函数调用时,必须提供与形参个数相同、顺序相同、类型相同的实参;或通过类型转换能够将实参的值映射为形参类型的值。在C++中,可以使用两种传递机制将实参的值传递给形参: 一种被称为值传递(值调用)。 另一种被称为引用传递(引用调用)。面向对象程序设计第二版第五十八页,共八十四页,2022年,8月28日5.6.1值调用C++中变量的值有两种:变量本身值和变量地址值。1.传值调用的特点:调用时系统先计算实参表达式的值,然后依次赋给形参。因此,传值调用的实现机制是系统将实参拷贝一个副本给形参。在被调用的函数中参数(即参数的副本)被改变,但未改变实参的值(即不能“回带”)。例如:交换两个变量a和b的值(采用传值调用)。voidf_swap1(intA,intB){ inttemp; temp=A; A=B; B=temp; cout<<“A=”<<A<<”“<<”B=”<<B<<endl;}
面向对象程序设计第二版第五十九页,共八十四页,2022年,8月28日voidmain(){ inta=5,b=6; f_swap1(a,b);//执行完该语句后,变量的值没有改变 cout<<“a=”<<a<<“”<<“b=”<<b<<endl;}运行结果如下:A=6 B=5//函数内的结果a=5 b=6//主函数中的结果,可见通过传值调用//a、b的 值并没有被“回带”面向对象程序设计第二版第六十页,共八十四页,2022年,8月28日2.传址调用的特点使用传址调用方式时,调用函数的实参是某个变量的地址值,被调用的函数的形参是指针(变量的指针或类类型的变量的指针)。请注意,不能通过return返回或者通过参数“回带”在函数中定义的局部变量的地址(或指针)。例如,通过函数返回、指针参数和引用参数来得到函数中局部变量的值。下面的做法结果是错误的:int*f_pIntandInt(int*y,int&x){ inta=5; int*s=&a; y=s;//y=&a; x=a; returns;}面向对象程序设计第二版第六十一页,共八十四页,2022年,8月28日voidmain(){ inta=6,b=7; int*pa=&a; int*x=f_pIntandInt(pa,b); //执行完该语句后,变量的值将被改变 cout<<”*x=“<<*x<<endl; cout<<”*pa=”<<*pa<<endl; cout<<”b=”<<b<<endl;}运行结果如下: *x=351 //*x不是5而是个随机数 *pa=6 //*pa也不是5且未被修改 b=5 //“回带”了值5面向对象程序设计第二版第六十二页,共八十四页,2022年,8月28日5.6.2引用调用使用引用作为函数的形参时,调用函数的实参要用变量名,将实参的变量名传给形参的引用,相当于在被调用函数中使用了实参的别名。例如:交换两个变量a和b的值。(采用引用调用)voidf_swap3(int&A,int&B){ inttemp; temp=A; A=B; //交换两个引用变量中的内容(改变实参值) B=temp; cout<<“A=”<<A<<”“<<”B=”<<B<<endl;}面向对象程序设计第二版第六十三页,共八十四页,2022年,8月28日voidmain(){ inta=5,b=6; f_swap3(a,b);//变量的地址没变,但是变量的值被间接改变 cout<<“a=”<<a<<”“<<”b=”<<b<<endl;}运行结果如下: A=6B=5//函数内的结果 a=6b=5//主函数中的结果,可见引用调用a、b的值被“回带”建议:在需要“回带”值的时候,使用引用。面向对象程序设计第二版第六十四页,共八十四页,2022年,8月28日5.7函数的参数
5.7.1函数参数的求值顺序
当一个函数带有多个参数时,C++语言没有规定在函数调用时对实参的求值的顺序,编译器根据对代码进行优化的需要自行决定对实参的求值顺序。有的编译器规定自左向右,有的编译器规定自右向左,这种求值顺序的不同,对一般参数来讲没有影响。但是,如果实参表达式中的某个变量与前后表达式中的某个变量有关时,就可能由于求和顺序的不同而造成了二义性。因此,建议将参数列表简单化为好(相关运算放在调用前)。(举例如下)面向对象程序设计第二版第六十五页,共八十四页,2022年,8月28日intf_add_int(intx,inty,intz){ returnx+y+z;}voidmain(){ intx=5,y=6,z=7,c; c=f_add_int(++x,x+y+z,--z);}如果编译器规定自左向右的求值,则
c=f_add_int(6,6+6+7,6),c=31;如果编译器规定自右向左的求值,则 c=f_add_int(6,5+6+6,6),c=29;面向对象程序设计第二版第六十六页,共八十四页,2022年,8月28日5.7.2数组作为函数参数数组作为函数参数时,根据调用机制的不同,可以分为两种情况:⒈形参用数组,实参用数组名调用函数的实参用数组名,被调用的函数的形参用数组,这种调用的机制是形参和实参共用内存中的同一个数组。例如:检查数组A[m],将数组中小于0的数改写为0;voidf_rw1(intA[],intn)//定义时形参要标明数组元素的类型、数组名{ for(inti=0;i<n;i++) { if(*(A+i)<0) { *(A+i)=0; } }}面向对象程序设计第二版第六十七页,共八十四页,2022年,8月28日voidmain(){ inta[4]={1,-5,4,-3}; intn=4; f_rw1(a,4); //调用时实参只写数组名}结果是a中的元素变为:1,0,4,0面向对象程序设计第二版第六十八页,共八十四页,2022年,8月28日⒉形参用指针实参用数组名
调用函数的实参用数组名,被调用的函数的形参用指针,这种调用的机制是实参把数组的名字传递给形参。例如:检查数组A[m],将数组中小于0的数改写为0;voidf_rw2(int*A,intn) //形参用指针{ for(inti=0;i<n;i++) { if(*(A+i)<0) { *(A+i)=0; } }}voidmain(){inta[4]={1,-5,4,-3};intn=4;f_rw2(a,4); //调用时实参只写数组名}面向对象程序设计第二版第六十九页,共八十四页,2022年,8月28日5.7.3带缺省值的函数C++语言允许在函数的原型中(若没有说明函数的原型,则应在函数定义时),给一个或多个参数指定默认值。但是,在指定了默认值的参数右边不能出现没有指定默认值的参数。在函数调用时,编译器按从左到右的顺序将实参传给形参,当实参的数目不足时,编译器将按同样的顺序用函数定义中的默认值来补充所缺少的实参。如:正确的带缺省值函数的原型是:voidf_a(intx,inty=8,intz=9);
错误的带缺省值函数的原型是:voidf_a(intx=5,inty,intz=6);voidf_a(intx=5,inty=6,intz);注意:C++不允许在函数原型和函数定义中,同时指定默认值。
面向对象程序设计第二版第七十页,共八十四页,2022年,8月28日5.8静态成员函数
为了在不生成类对象的情况下,直接访问静态数据成员,C++定义了静态成员函数的概念。定义静态成员函数的格式如下: static返回值类型成员函数名(参数列表);在静态成员函数中所引用的数据,可以是以下两种类型:⒈直接引用:类中的静态数据成员;全局数据变量;用const关键字说明的常量;面向对象程序设计第二版第七十一页,共八十四页,2022年,8月28日⒉间接引用:
类中的非静态数据成员,可以通过该类的对象间接引用。如下例:intex=6;constintcx=7;classMyclass{public: staticintf_1(Myclassm);//静态成员函数 intx; //非静态数据成员 staticinty;//静态数据成员};intMyclass::y=8;intMyclass::f_1(Myclassm){ m.x=9; returny+ex+cx+m.x;}面向对象程序设计第二版函数的调用结果是:返回值=30
第七十二页,共八十四页,2022年,8月28日访问静态成员函数的方法有如下几种:
方法⒈类名::静态成员函数名
方法⒉对象名.静态成员函数名
方法⒊对象指针->静态成员函数名面向对象程序设计第二版第七十三页,共八十四页,2022年,8月28日5.9函数的重载
所谓函数重载是指相同的函数名下,可以实现不同的操作。系统将根据参数类型或者参数个数的不同来区分这些重载的函数。用户在调用时,只要给出不同类型的参数或者不同个数的参数。编译器就能区别你要调用哪个函数。函数重载的两个条件: ⒈函数名相同; ⒉函数参数的类型不同或者参数的个数不同。面向对象程序设计第二版第七十四页,共八十四页,2022年,8月28日例如编写两个数据相加的程序(其中这两个
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度在线教育平台合作利益分配及教学资源共享合同4篇
- A4尺寸个性化离婚合同模板2024版B版
- 二零二五年度海洋旅游码头场地租赁及旅游产品开发合同4篇
- 2025年度物业合同顺延及社区安全防范补充协议
- 二零二五年度带智能家居音响系统个人房屋租赁简版合同
- 2025年度酒店住宿高尔夫球场预订合同
- 二零二五年度解除劳动合同关系及员工档案管理协议
- 2025年度房屋按揭贷款个人信用保证合同
- 二零二五年度艺人影视作品宣传推广经纪合同与劳动合同
- 二零二五年度土地开发与使用权出让合同
- 土地买卖合同参考模板
- 新能源行业市场分析报告
- 2025年天津市政建设集团招聘笔试参考题库含答案解析
- 房地产运营管理:提升项目品质
- 自愿断绝父子关系协议书电子版
- 你划我猜游戏【共159张课件】
- 专升本英语阅读理解50篇
- 中餐烹饪技法大全
- 新型电力系统研究
- 滋补类用药的培训
- 北师大版高三数学选修4-6初等数论初步全册课件【完整版】
评论
0/150
提交评论