资源预览内容
第1页 / 共122页
第2页 / 共122页
第3页 / 共122页
第4页 / 共122页
第5页 / 共122页
第6页 / 共122页
第7页 / 共122页
第8页 / 共122页
第9页 / 共122页
第10页 / 共122页
亲,该文档总共122页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第11章类1面向过程和面向对象设计方法n(1)面向过程程序设计基本思想面向过程程序设计基本思想自顶向下逐步求精模块化设计结构化编码n问题:验证哥德巴赫猜想任意一个大于2的偶数都可以分解成两个素数之和。1面向过程和面向对象设计方法n自顶向下、逐步求精每个大于2的偶数n能否分解成两个素数之和;选一个小于n的素数x,判断n-x是否为素数;怎样判断一个数是素数;怎样判断一个数是否为另一个数所整除;n模块化设计、结构化编码用函数模块实现各个小功能;主函数模块main按照程序设计逻辑顺序直接调用各个函数模块,实现程序功能;1面向过程和面向对象设计方法n衡量面向过程程序设计的指标:内聚度n指同一个模块中各个步骤之间的关联程度;也就是函数的重用度较高;耦合度n表示被调用函数与调用函数之间的接口复杂程度;n质量高的程序:内聚度高内聚度高,耦合度低耦合度低;1面向过程和面向对象设计方法n结构化程序设计的缺点:程序设计以功能为中心(而不是以数据为中心)、按步骤来进行。程序由一组相互协作的函数组成;数据与处理数据的函数之间是分离的;很难同时做到高内聚低耦合;大型软件的编写比较复杂,软件开发和维护的费用比较高软件危机问题;1面向过程和面向对象设计方法n(2)面向对象程序设计面向对象程序设计将数据和对数据进行操作(输入、访问、修改、输出等)的函数绑定封装在一个称为类的数据类型中。程序设计以数据为中心,程序由一组相互协作的对象组成。客观世界中任何一个事物都可以看成一个对象n学校是一个对象,一个班级也是一个对象,一个学生也是一个对象。对象具有两个要素:n属性n行为实体概念对象现象类建模具体抽象模拟参照系统面向对象程序面向对象程序设计过程面向对象程序设计过程C+类类是面向对象程序设计的核心,它实际上是一种新的数据类型。类:类将不同类型的数据和这些数据相关的操作封装在一起的集合体,类具有更高的抽象性,类中的数据具有隐藏性和封装性。类包括:描述属性的数据和处理数据的方法(行为)。对象是类的实例。对象将其属性和行为封装在一起,并将其内部大部分的实现细节隐藏起来,仅通过一个可控的接口与外界交互。面向对象程序设计的特点一、抽象性抽象是对具体对象(问题)进行概括,抽出抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。这一类对象的公共性质并加以描述的过程。1.先注意问题的本质及描述,其次是实现过程或细节。2.数据抽象:描述某类对象的属性或状态。3.代码抽象:描述某类对象的共有的行为特征或具有的功能。4.抽象的实现:通过类的声明。抽象实例钟表n数据抽象:intHour,intMinute,intSecondn代码抽象:SetTime(),ShowTime()抽象实例钟表类classClockpublic:voidSetTime(intNewH,intNewM,intNewS);voidShowTime();private:intHour,Minute,Second;二、封装将抽象出的数据成员、代码成员相结合,将它们视为一个将抽象出的数据成员、代码成员相结合,将它们视为一个整体。整体。目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。实现封装:在c+中,利用类的形式实现封装的。n实例:classClockpublic:voidSetTime(intNewH,intNewM,intNewS);voidShowTime();private:intHour,Minute,Second;边界边界特定的访问权限外部接口外部接口类实现了对数据的有效隐蔽和合理控制,是类实现了对数据的有效隐蔽和合理控制,是问题的属性和行为的有机和谐的统一。问题的属性和行为的有机和谐的统一。三、继承与派生是C+中支持层次分类的一种机制,允许程序员在保持原有类特性的基础上,进行更具体的说明。新的类由原有的类产生,我们说新类继承了原有类的特征,或者说原有类派生出新类。四、多态性n多态:是指类中具有相似功能的不同函数使用同一个名称来实现的现象。多态:是指类中具有相似功能的不同函数使用同一个名称来实现的现象。只是人类思维方式的一种直接模拟,比如说“打球”的这个“打”,就是一个多态现象。打篮球、打羽毛球等规则和实际“打”的操作相差甚远,只是功能相似,我们就统一使用“打”来表示。n目的:达到行为标识统一,减少程序中标识符的个数。目的:达到行为标识统一,减少程序中标识符的个数。n实现:重载函数和虚函数实现:重载函数和虚函数从结构到类从结构到类结构:单纯堆积数据空间构造的类型类:不但描述数据空间,还描述对数据的操作变量:由基本数据类型或自定义类型所产生的实体对象: 由类产生的实体,本质上,变量也是对象.定义结构n实际问题中涉及大量的复合数据,这些复合数据最初是由struct来描述和堆积起来的,如日期类型由年、月、日三个整型数据量表示,描述为:structDateintyear;intmonth;intday;nstruct结构只能算是一种粗糙的数据类型,因为严格意义下的数据类型,不但要有数据的内部表示以及表示范围,还要有数据的操作,即如何代表复合数据的整体来访问它们,而上面的struct描述并没有涉及其操作。nC+的内部数据类型全是由小写字母组成的,因为它们是保留字,用struct定义的数据类型属于人为定义的类型名称,通常采用大写字母开头的命名方法来加以区分。nstruct定义的复合数据中,其成员还可以是struct型的,即可以嵌套,如一个平面上的圆一般由中心坐标和半径构成:structCirclePointcentre;/Point是struct型doubleradius;n操作struct型数据的方式,一般是先定义结构型变量,然后对该变量成员进行访问。如定义一个日期型结构变量,赋值为2005年12月6日,判断其是否闰年输出,程序如下:n/日期结构n/=n#includen#includenusingnamespacestd;n/-nstructDatenintyear;nintmonth;nintday;n;/-nvoidprint(Date);nboolisLeapYear(Dated);n/-nintmain()nDated;nd.year=2000;nd.month=12;nd.day=6;nif(isLeapYear(d)nprint(d);n/-nvoidprint(Dates)ncout.fill(0);ncoutsetw(4)s.year-setw(2)s.month-setw(2)s.dayn;ncout.fill();n/-nboolisLeapYear(Dated)nreturn(d.year%4=0&d.year%100!=0)|(d.year%400=0);n/=n用struct定义的类型没有相关操作,为了给日期数据赋值或者重置日期,要在应用程序中单独编写赋值代码,或者为了输出日期和判断日期是否闰年,须单独编制日期输出函数print和isLeapyear,n创建这样的“数据类型”后,增加了编程的负担,因为所有的与结构体相关的操作都必要程序员承担。类nC+提供了一种类class机制,不但可以定义数据的复合,还可以定义该复合数据的操作。n类机制定义类class,类是一种类型type,定义类的格式与struct相像,只是在定义体中添上操作,操作是一个个的功能,由函数形式表示。n如在定义日期型时,同时将日期的相关操作也一并描述:n#includen#includenusingnamespacestd;nclassDatenintyear,month,day;npublic:nvoidset(inty,intm,intd);/赋值操作nboolisLeapYear();/判断闰年nvoidprint();/输出日期n;/-nvoidDate:set(inty,intm,intd)nyear=y;month=m;day=d;n/-nboolDate:isLeapYear()nreturn(year%4=0&year%100!=0)|(year%400=0);n/-nvoidDate:print()ncoutsetfill(0);ncoutsetw(4)year-setw(2)month-setw(2)dayn;ncoutsetfill();n/-nintmain()nDated;nd.set(2000,12,6);nif(d.isLeapYear()nd.print();n/=Class是一种数据类型,其变量为对象.对象包含有membervariables(成员变量)和memberfunctions(成员函数).11.1类的定义类是一种用户自定义类型,声明形式:class类名称public:公有成员和函数(外部接口)private:私有成员和函数protected:保护型成员和函数在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。与private类似,其差别表现在继承与派生时对派生类的影响不同,第16章讲。classClockpublic:voidSetTime(intNewH,intNewM,intNewS);voidShowTime();private:intHour,Minute,Second;成员数据:成员数据:与一般的变量声明相同,但需要与一般的变量声明相同,但需要将它放在类的声明体中。将它放在类的声明体中。成员函数:成员函数:在类中说明原形,可以在类外给出在类中说明原形,可以在类外给出函数体实现,并在函数名前使用类名加以限定。函数体实现,并在函数名前使用类名加以限定。也可以直接在类中给出函数体,形成也可以直接在类中给出函数体,形成内联成员内联成员函数。函数。允许声明重载函数和带默认形参值的函数允许声明重载函数和带默认形参值的函数voidClock:SetTime(intH,intM,intS)Hour=H;Minute=M;Second=S;voidClock:ShowTime()coutHour:Minute:Second;11.2对象的生成类的对象是该类的某一特定实体,即类类型的变量。n声明形式:类名对象名;n例:ClockmyClock;类中成员的访问方式n类中成员互访直接使用成员名n类外访问使用“对象名.成员名”方式访问public属性的成员例11.1类的应用举例#includeclassClock/时钟类的声明时钟类的声明public:/外部接口,公有成员函数外部接口,公有成员函数voidSetTime(intNewH,intNewM,intNewS);voidShowTime();private: /私有数据成员私有数据成员intHour,Minute,Second;/时钟类成员函数的具体实现时钟类成员函数的具体实现voidClock:SetTime(intH,intM,intS)Hour=H;Minute=M;Second=S;voidClock:ShowTime()coutHour:Minute:Secondendl;voidmain()ClockmyClock;myClock.Hour=20;/?myClock.Minute=40;/?myClock.Second=26;/?coutmyClock.Hour:myClock.Minute:myClock.Secondendl;voidmain()ClockmyClock;myClock.SetTime(20,40,26);myClock.ShowTime();优点:1、允许类设计人员检查赋予数据成员的任何数值的有效性。程序其它部份无法给它们赋值,只有通过初始化函数或构造函数。2、在类中可以随意改变这些数据结构的设计而不影响其它程序。类的定义n类的定义一般分为说明部分和实现部分。说明部分数据成员说明 属性成员函数说明 方法类的成员函数的实现有两种方式:n在类中边定义边实现。n在类中定义在类外实现。请先记住有这两种方式,具体如何实现后面会讲到。类的一般定义格式class public: private: ;/分号不能忘 n其中,class是定义类的关键字。n从访问权限上来分,类的成员可以分为三种:公有的(public),私有的(private)和保护的(protected)。publicprivateprotected可以被同类的方法和类之外的方法随意调用只能被同类中的方法访问,其它类都不能访问只能被同类和子类的方法访问,其余访问均会遭到拒绝classDatepublic:voidSetDate(inty,intm,intd);intIsLeapYear();/只有定义,没有实现,需在类外实现voidPrint()coutyear“.”month“.”dayendl;/在类中边定义边实现private:intyear,month,day;/三个私有成员变量;函数名往往是由多个单词组成的,习惯约定每个单词的第一个字母用大写字母,其余用小写,单词间不加任何分隔符。那么,函数在类外如何实现呢?Date类的定义类体外实现成员函数的格式:返回类型:():称为作用域运算符,用来标识某个成员函数是属于哪个类的。voidDate:SetDate(inty,intm,intd)year=y;month=m;day=d;intDate:IsLeapYear()return(year%4=0&year%100!=0)|(year%400=0);对象的定义类定义完后如何使用呢?答案是通过对象,对象是类的实例。对象的定义格式:对象名表可以一个或多个对象名,多个时用逗号分隔。可以是一般的对象名,还可以是指向对象的指针和对象数组。Datedate1,date2,*Pdate,date31;date1,date2是一般的对象名,*Pdate是指向对象的指针,date是对象数组的数组名,它有31个元素,每个元素都是一个Date类的对象。对象成员的表示方法一个对象的成员就是该对象的类所定义的成员。一般对象表示如下: .或 .() .是一个运算符,表示对象的成员。 date1.year; date1.SetDate(int y,int m,int d); 但是Pdate.year却是错的,为什么呢? 对象指针的成员表示法: -或者 -() -也是一个表示成员的运算符,与.的区别是: -是用来表示指向对象的指针的成员,而.用来表示一般对象的成员。 Pdate-year Pdate-SetDate(int y,int m,int d)下面两种定义是等价的:-(*).Pdate-SetDate(y,m,d)(*Pdate).SetDate(y,m,d)定义类时应注意事项在类体中不允许对所定义的数据成员进行初始化。例如: class Date public: private: int year(1998),month(4),day(9); ;是错的。n类中的数据成员的类型可以是任意的,包含整型,浮点型,字符型,数组和引用等。也可以是对象,另一个类的对象,但是自身类的对象是不可以的,而自身类的指针或引用是可以的。n一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明。n成员函数可以重载,但必须满足重载条件:参数类型不同或参数个数不同。开发一个C+程序的过程源程序源程序目标程序目标程序可执行代码可执行代码库库编译程序链接程序.cpp.hpp.obj.exe1.编辑编辑2.编译编译3.链接链接 4.运行运行编程风格n一行代码只写一条语句;n尽可能在定义变量的同时对其初始化(就近原则);n程序的分界符和应独占一行并且位于同一列,同时与引用它们的语句左对齐;n通过在程序的每一行开头键入不同次数的TAB键进行缩进,以体现程序的层次结构;编程风格n标识符的命名尽量做到见其名知其义,且不同类型和不同属性的标识符遵循下述不同的命名法则:局部变量名用小写,变量名单词之间用下划线连接;如果局部变量为指针变量,则变量名以小写p开头;如果局部变量为引用变量,则变量名以小写r开头;全局变量名各单词的第一个字母用大写,变量名单词之间用下划线连接;如果全局变量为指针变量,则变量名以大写P开头;常量标识符全部大写;编程风格函数名第一个单词小写,之后各单词的第一个字母大写,单词之间不用下划线连接;结构名以大写S开头,之后各单词的第一个字母大写,单词之间不用下划线连接;联合名以大写U开头,之后各单词的第一个字母大写,单词之间不用下划线连接;枚举名以大写E开头,之后各单词的第一个字母大写,单词之间不用下划线连接;类名以大写C开头,之后各单词的第一个字母大写,单词之间不用下划线连接;成员函数定义成员函数与普通函数的区别:n成员函数属于类,成员函数定义是类设计的一部分,其作用域是类作用域.而普通函数一般为全局函数n成员函数的操作主体是对象,使用时通过捆绑对象来行使其职责,而普通函数被调用时没有操作主体n函数定义体的花括号对的后面是没有分号的,而类定义体的花括号对的后面是有分号的。原因是因为class机制是自定义类型的机制,是在struct的语法基础上改建的,必须和struct对应,所以存在着分号。成员函数的内联性成员函数可以在类内,也可以在类外定义,若在类内定义,则默认为内联函数.但编译器对内联的接受自有其原则.classDateintyear,month,day;public:voidset(inty,intm,intd)/默认内联year=y;month=m;day=d;inlineboolDate:isLeapYear()/显式内联return!(year%400)|!(year%4)&year%100;使用对象指针n一个类可以创建无数个对象,其任何一个对象都可以使用该类的操作,即调用该类的成员函数,不同对象调用成员函数的结果是不同的,所以,调用成员函数一定是与某个对象绑定在一起,因此调用成员函数的形式为:nbjectName.memberFunctionName(parameters)n如果对象是以对象指针间接访问的形式操作的,则对象与成员函数之间就用双字符的箭头”-”,即:nobjectPointer-memberFunctionName(parameters);n或者将对象指针的间访形式用括号括起来,再加点操作符”.”加成员函数,即:n(*objectPointer).memberFunctionName(parameters);访问成员函数的方式对象方式对象方式Dated;d.set(2005,12,5);对象指针方式对象指针方式Date*dp=newDate;dp-set(2005,12,5);/deletedp;重载成员函数n成员函数与普通函数一样,可以重载,对重载的识别和使用规则也是相同的,如在前面的日期类基础上,增加以一个string参数来设置年、月、日值的set成员函数:n/overloadmemberfunctionsn/=n#includen#includenusingnamespacestd;n/-nclassDatenintyear,month,day;npublic:nvoidset(inty,intm,intd);nvoidset(conststring&s);nboolisLeapYear();nvoidprint();n;/-nvoidDate:set(inty,intm,intd)nyear=y;month=m;day=d;n/-nvoidDate:set(conststring&s)nyear=atoi(s.substr(0,4).c_str();nmonth=atoi(s.substr(5,2).c_str();nday=atoi(s.substr(8,2).c_str();n/-nboolDate:isLeapYear()nreturn(year%4=0&year%100!=0)|(year%400=0);n/-nvoidDate:print()ncoutsetfill(0);ncoutsetw(4)year-setw(2)month-setw(2)dayn;ncoutsetfill();n/-nintmain()nDated,e;nd.set(2000,12,6);ne.set(2005-05-05);ne.print();nif(d.isLeapYear()nd.print();n/=类的程序结构类定义作为头文件,如:point.h类的实现作为独立编译单元,如:point.cpp使用类的程序作为另一独立编译单元,如:f0809.cpp类的头文件和类的实现可以作为一个独立的资源提供给编程者内联的成员函数定义一般放在头文件中n/point.hn/=n#ifndefHEADER_POINTn#defineHEADER_POINTn#includenusingnamespacestd;n/-nclassPointnintx,y;npublic:nvoidset(inta,intb);nPointoperator+(constPoint&d);nfriendostream&operator(ostream&o,constPoint&d);n;/=ninlineostream&operator(ostream&o,constPoint&d)nreturno(d.x,d.y)n;n/-n#endif/HEADER_POINTn/point.cppn/=n#includepoint.hn/-nvoidPoint:set(inta,intb)nx=a,y=b;n/-nPointPoint:operator+(constPoint&d)nPoints;ns.set(x+d.x,y+d.y);nreturns;n/-n/programorganizationforclasssn/=n#includepoint.hn#includenusingnamespacestd;n/-nintmain()nPoints,t;ns.set(2,5);nt.set(3,1);ncouts+t;n/=屏蔽类的实现使用类的应用程序只需要类定义头文件编程实现类,也只需要类定义头文件,不需要使用类的程序细节确定了类定义(头文件),便可以从事两方面的编程而互不干涉类定义成功地屏蔽了类的实现,是类机制的技术体现n如定义了一个平面坐标点类PPoint,其类定义文件为ppoint.h,其类的实现文件为ppoint.cpp,使用该类的应用程序为f0811.cpp,使用该类以计算坐标点的直角坐标x和y分量以及极坐标a和r分量程序如下:n/=n/ppoint.hn/=n#ifndefHEADER_PPOINTn#defineHEADER_PPOINTnclassPPointndoublex,y;/直角坐标抑或极坐标npublic:nvoidset(doubleix,doubleiy);/设置坐标ndoublexOffset();/直角坐标x分量ndoubleyOffset();/直角坐标y分量ndoubleangle();/极坐标弧角ndoubleradius();/极坐标半径n;/=n#endif/HEADER_PPOINTn/=n/ppoint.cppn/直角坐标版本n/=n#includeppoint.hn#includenusingnamespacestd;n/-nvoidPPoint:set(doubleix,doubleiy)nx=ix;y=iy;n/-ndoublePPoint:xOffset()nreturnx;n/-ndoublePPoint:yOffset()nreturny;n/-ndoublePPoint:angle()nreturn(180/3.14159)*atan2(y,x);n/-ndoublePPoint:radius()nreturnsqrt(x*x+y*y);n/-n/=n/f0811.cppn/usingPPointclassn/=n#includeppoint.hn#includenusingnamespacestd;n/-nintmain()nPPointp;n/重复输入x和y轴分量,直到x分量值小于nfor(doublex,y;coutxy&x=0;)np.set(x,y);ncoutangle=p.angle(),radius=p.radius()n,xoffset=p.xOffset(),yoffset=p.yOffset()n;nn/=n这样的实现方法,使得类的程序员可以不管类的具体实现,而类实现的编码者也可以在适当的时候对类进行改写或者升级,如ppoint类的另外一个版本:n/极坐标版本n/=n#includeppoint.hn#includenusingnamespacestd;n/-nvoidPPoint:set(doubleix,doubleiy)nx=atan2(iy,ix);ny=sqrt(ix*ix+iy*iy);n/-ndoublePPoint:xOffset()nreturny*cos(x);n/-ndoublePPoint:yOffset()nreturny*sin(x);n/-ndoublePPoint:angle()nreturn(180/3.14159)*x;n/-ndoublePPoint:radius()nreturny;n/-n这个版本将类的实现改变了,成员函数的定义体改变了,但是类定义没有变,这就使得应用程序无须做任何变动,只要重新编译一下就可以在同样的正确性下工作。影响编程方法n世界上有许多通用的概念,都可以实现为数据类型,如日期、时间、人、学生等。许多待解决的问题,都与这些通用的概念有关,因此,把它们做成大众化的数据类型,作为整型和浮点型数据类型的延伸,通过这些使用大众化的数据类型,就可以提高编程的效率,更高超的是,将处理所有的数据类型的对象集合做成数据类型,形成处理通用类型的数据类型,称为模板。影响语言设计nC+拥有强有力的类型检查能力,能正确无误的识别类型、对象、成员、变量、公有和私有以及交错的各种作用域,保证了编程的安全性。n类的数据封装,提高了对象访问的安全性,用户不能直接操作对象中的数据分量,只能以对象的身份去使用操作,影响对象的值,从而职责分明。C+程序结构nmain.cppnclass.cppnfunction.cppC+程序结构nmain.cpp程序文件#include#include#include自定义类库头文件#include自定义函数头文件“函数原型全局数据定义intmain().函数定义C+类n成员函数与普通函数n保护成员与屏蔽类的内部实现n类的作用域与可见性12对象的初始化在类中有两个特殊类型的成员函数:构造函数constructor析构函数destructor一、构造函数n构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。n在对象创建时由系统自动调用。n如果程序中未声明,则系统自动产生出一个默认形式的构造函数n允许为内联函数、重载函数、带默认形参值的函数1.构造函数举例classClockpublic:Clock(intNewH,intNewM,intNewS);/构造函数voidSetTime(intNewH,intNewM,intNewS);/初始化成员函数voidShowTime();private:intHour,Minute,Second;构造函数的实现:Clock:Clock(intNewH,intNewM,intNewS)Hour=NewH; Minute=NewM;Second=NewS;建立对象时构造函数的作用:voidmain()Clockc(0,0,0);/隐含调用构造函数,将初始值作为实参。c.ShowTime();注:、构造函数与类同名,定义构造函数时,不能指定返回值。、构造函数在生成类对象时自动调用。、new操作符自动调用生成对象的构造函数。Clock*myclock=newClock(10,25,10);这是new比malloc函数的一个重要优势。2、缺省构造函数缺省构造函数:没有参数的构造函数。缺省构造函数初始化数据成员时常赋予缺省值。classClockpublic:Clock()Hour=Minute=Second=0;/缺省构造函数Clock(intNewH,intNewM,intNewS);/构造函数voidSetTime(intNewH,intNewM,intNewS);voidShowTime();private:intHour,Minute,Second;注:如果没有定义特定类的构造函数,则编译器会自动生成缺省构造函数。如果类有缺省构造函数(用户定义或编译器生成),则可以不传递参数而定义类对象。例:Clockmyclock;如果不向构造函数传递参数,不要在对象定义中加上空括号,否则系统会声明一个返回类类型的函数而不是定义类实例。Clockmyclock();/声明函数myclock.Clockmyclock(10,12,50)如果定义了类的构造函数,则编译器不再生成缺省构造函数。因此,如果定义了一个或几个构造函数,但其中不包括缺省构造函数,则类不会有缺省构造函数。注意:使用没有缺省构造函数的类有时会产生错误。(数组初始化)不允许在类定义中初始化数据成员。下列类定义是错误的classcprivate:intHour=10,Minute=20,Second=30;3、成员初始化表成员初始化表应放在构造函数定义中参数表的紧后方,包括冒号和一个或几个用逗号隔开的项。每一项包括数据成员名和括号内的初始化值。例如:classClockpublic:Clock()Hour=Minute=Second=0;Clock(intNewH,intNewM,intNewS):Hour(NewH),Minute(NewM),Second(NewS);voidShowTime();private:intHour,Minute,Second;生成对象Clockcobject(10,20,30)则对象cobject中,成员Hour,Minute,Second分别初始化为10,20,30。4、重载构造函数classclockprivate:inthours,minutes,seconds;public:Clock()Hour=Minute=Second=0;Clock(intNewH,intNewM,intNewS)Hour=NewH;Minute=NewM;Second=NewS;voidmain()clockmyclock1;clockmyclock2(25,25,25);和重载全局函数一样,也可以重载类构造函数和类中除析构函数以外的任何其它成员函数。例5、对象成员(子对象)可以定义一个类作为另一个类的数据成员,即可以将一个类的对象嵌入另一个类可以定义一个类作为另一个类的数据成员,即可以将一个类的对象嵌入另一个类的对象中。这种数据成员称为对象成员。的对象中。这种数据成员称为对象成员。有子对象的构造函数:有子对象的构造函数: 构造函数名(参数表):子对象名(参数表)构造函数名(参数表):子对象名(参数表)classccontprivate:cembededemb;/对象成员对象成员public:ccont(intP1,intP2,intP3):emb(P1,P2)/对象成员初始化对象成员初始化coutp3endl;voidmain()ccontc(1,2,3);classcembededpublic:cembeded(intp1,intp2)coutp1“,”p2endl;;6、成员函数的定义类中的成员函数,可在类中定义,也可在类中说明,在类外定义。在类外定义成员函数:函数值类型类名:函数名(参数)可以减少类体的长度,使类体清晰,便于阅读,而且有助于把类的接口和类的实现细节分离。例11.6p1097.内联成员函数总结:总结:1、在类定义外定义成员函数时,必须在函数名前加上类名和、在类定义外定义成员函数时,必须在函数名前加上类名和作用域操作符作用域操作符:。2、类内外定义的函数之间有个重要的区别:类内定义的函数、类内外定义的函数之间有个重要的区别:类内定义的函数被当作内联函数,而类外定义的函数缺省当作非内联函数。被当作内联函数,而类外定义的函数缺省当作非内联函数。短函数可以定义在类短函数可以定义在类?,长函数定义在,长函数定义在?。3、如果也想把类外定义的函数也当作内联函数,可用、如果也想把类外定义的函数也当作内联函数,可用inline关键字。关键字。成员函数定义在类内和类外有个重要区别:类内定义的函数成员函数定义在类内和类外有个重要区别:类内定义的函数被当做内联函数,而类外定义的函数缺省当做非内联函数。被当做内联函数,而类外定义的函数缺省当做非内联函数。若要把类外定义的函数当做内联函数,可以用若要把类外定义的函数当做内联函数,可以用inline关键字。关键字。inline函数值类型类名:函数名(参数)#includeiostream.hinta=0,b=0;classApublic:inta,b;voidf(intc=2,intd=3)a=c;b=d;voidf(intc=3,intd=4)a=c;b=d;voidmain()An;n.f();coutn.an.bendl;f();coutn.an.bendl;n.f(7,8);coutab”,ptr-showtime();用new也可以动态生成一个对象,生成对象时自动调用构造函数Clock*ptr=newClock;deleteptr;释放释放voidset(char*string)deleteBuffer;Buffer=newcharstrlen(string)+1;strcpy(Buffer,string);voidmain()CMessagem;m.set(hello);m.display();1、析构函数:用来释放对象,在对象删除前,用它来做一、析构函数:用来释放对象,在对象删除前,用它来做一些清理工作,它在类对象销毁时自动调用。些清理工作,它在类对象销毁时自动调用。二.析构函数#includeiostream.h#includestring.hclassCMessageprivate:char*Buffer;public:CMessage()Buffer=newchar(0);CMessage()deleteBuffer;voiddisplay()coutBuffer;注:(1)析构函数与类同名,前面加上号。(2)和构造函数一样,析构函数不能定义返回类型。(3)与构造函数不同的是,析构函数不能接收参数。(4)如果类中没有定义析构函数,编译系统会自动生成缺省析构函数(空函数)。(5)析构函数不能重载。(6)一个类中只能定义一个析构函数。2、何时调用构造函数和析构函数。构造函数在对象生成时调用析构函数在对象销毁时调用特定类型对象构造函数和析构函数的调用:对全局定义的对象:构造函数在程序开始运行时调用,即在main接到控制前调用。析构函数在程序结束时调用。对于局部定义的对象:构造函数在控制流到达对象定义时调用,析构函数在控制传出定义对象的块时(即包含对象的函数退出时)调用。(3)对于用static关键字局部定义的对象:构造函数在控制首次到达对象定义时调用,析构函数在程序结束时调用。(4)对于用new操作符动态生成的对象:构造函数在对象生成时调用,析构函数在对象用delete操作符明确销毁时调用(如果不明确销毁对象,则析构函数永远也不调用)。#include“iostream.h”/example11.8#includestring.hvoidfun();classAApublic:AA(char*str)string=newcharstrlen(str)+1;strcpy(string,str);coutConstrstringendl;AA()coutDestrustringendl;deletestring;private:char*string;AAb(globalobject);voidmain()AA*a;a=newAA(newobject);fun();deletea;voidfun()coutcomeinfunendl;AAaa(functionobject);staticAAbb(staticobject);coutgooutfunendl;运行结果:ConstrglobalobjectConstrnewobjectcomeinfunConstrfunctionobjectConstrstaticobjectgooutfunDestrufunctionobjectDestrunewobjectDestrustaticobjectDestruglobalobject调用构造函数和析构函数的顺序对于相同的对象:先构造的后析构,后构造的先析构,相当于一个栈,先进后出。分析下面程序的运行结果:例:classstudentpublic:student(intn,stringnam,chars)num=n;name=nam;sex=s;cout“Constructorcalled”endl;student()cout“Destructorcalled”numendl;voiddisplay()cout“num:”numendl;cout“name:”nameendl;cout“sex:”sexendl;private:intnum;charname10;charsex;voidmain()studentstu1(10010,”wang_li”,f);stu1.display();studentstu2(10011,”zhang_fun”,m);stu2.display();程序运行结果如下:程序运行结果如下:Constructorcallednum:10010name:wamg_lisex:fConstructorcallednum:10011name:zhamg_funsex:mDestructorcalled10011Destructorcalled10010判断以下程序的执行结果_.#includeclassApublic:A(intsma)coutAsmaendl;A()coutDes.endl;voidfn(intn)staticAsm(n);coutfnnendl;voidmain()fn(10);fn(20);(A)A10fn10fn20Des.(B)A10fn10Des.A20fn20Des.(C)A10A20fn10fn20Des.Des.(D)A10fn10fn20Des.Des.A判断以下程序的执行结果判断以下程序的执行结果#includeclassApublic:A()s=0;A(char*p)s=p;coutsn;A()coutsn;private:char*s;Aa1(hello);A&f()staticAa2(Beijing);Aa3(China);returna2;voidmain()for(inti=0;i2;i+)f();(C)helloBeijingChinaChinaBeijinghello(A)helloBeijingChinaChinaBeijingBeijingChinaChinaBeijinghello(B)helloBeijingChinaChinaChinaChinaBeijinghello(D)helloBeijingChinaChinaChinaChinahelloBeijingB1.4 静态(static)类成员一、静态数据成员1、定义:classCTestpublic;staticintcount;注:无论生成多少个CTest对象,只有一个count,因此它是由所有CTest对象共有的。静态数据成员只存储一处,供所有对象共用。2、初始化类型名类名:静态成员=初值;在类定义外且函数外初始化。intCTest:count=0;3、访问:直接用类名和作用域操作符voidmain()CTest:count=1;.4.在类中声明,可以控制对它的访问(public,private)voidAdd:GetNumber()coutNumber=Aendl;voidAdd:GetSum()coutSum=Sumendl;voidmain()AddM(3);M.GetNumber();M.GetSum();AddN(8);N.GetNumber();M.GetSum();N.GetSum();#includeiostream.hclassAddpublic:Add(inta);voidGetNumber();voidGetSum();intA;staticintSum;intAdd:Sum=0;Add:Add(inta)A=a;Sum+=A;二、静态成员函数1、定义:classCTeststaticintgetcount();2、调用:voidmain()intCount=CTest:getcount();.注:调用时用类名和作用域操作符,而不用类对象引用(类对象甚至可以不存在)。静态成员函数只能直接引用静态数据成员和属于其类的静态成员函数。静态数据成员和成员函数可用于维护类中的所有对象共享的数据项。#includeiostream.hclassCtestprivate:staticintcount;public:Ctest()+count;Ctest()-count;staticintGetcount()returncount;intCtest:count=0;voidmain()coutCtest:Getcount();coutobjectexistn;CtestTest1;Ctest*Ptest2=newCtest;coutCtest:Getcount();coutobjectexistn;deletePtest2;coutCtest:Getcount()coutobjectexistn;voidM:f(Mm)coutA=m.Aendl;coutB=Bendl;intM:B=0;voidmain()MP(5);M:f(P);MQ(10);M:f(P);M:f(Q);(4)如果静态成员函数中要引用非静态成员,可通过对象来引用。#includeiostream.hclassMpublic:M(inta)A=a;B+=10;staticvoidf(Mm);private:intA;staticintB;(1)const对象和const成员函数类对象定义中加入const表示不能改变该类中任何数据成员的值。类中的函数定义中加入const表示该函数不能改变任何数据成员的值。类名const对象名(实参表)或const类名对象名(实参表)const对象不能调用该对象的非const型的成员函数11.5const对象和const成员函数#includeclassClock/时钟类的声明时钟类的声明public:/外部接口,公有成员函数外部接口,公有成员函数Clock(intNewH,intNewM,intNewS);voidShowTime()const;private: /私有数据成员私有数据成员intHour,Minute,Second;/时钟类成员函数的具体实现时钟类成员函数的具体实现Clock:Clock(intNewH,intNewM,intNewS)Hour=NewH;Minute=NewM;Second=NewS;voidClock:ShowTime()constcoutHour:Minute:Secondendl;voidmain()constClockmyClock(20,40,26);myClock.ShowTime();注:常对象必须将其中的函数定义成常成员函数(构造函数除外),才能通过常对象调用常成员函数。(上例中,若ShowTime()不是常函数,则不能调用myClock.ShowTime();即使该函数并没有改变常参数,但编译器不允许。例:例:11.13#includeclassRpublic:R(intrl,intr2)Rl=rl;R2=r2;voidprint();voidprint()const;private:intR1,R2;voidR:print()coutR1:R2endl;voidR:print()const/常成员函数,重载coutR1;R2endl;voidmain()Ra(5,4);a.print();constRb(20,52);b.print();A:A(inti):a(i),r(a)voidA:print()couta“:”“:”rendl;voidmain()Aal(100),a2(0);al.print();a2.print();成员初始化表(2)常数据成员常数据成员只能通过构造函数中的成员初始化表的方式来对数据成员初始化。初始化后不能改。例11.14#includeclassApublic:A(inti);voidprint();constint&r;private:constinta;staticconstintb;constintA:b=10;this指针是隐含在指针是隐含在成员函数成员函数体中体中的指针,它指向调用该的指针,它指向调用该成员函数的对象。成员函数的对象。11.6this指针#includeiostream.hclassCTestpublic:intN;CTest(intn)N=n;intGetN()coutNNreturnN;voidmain()CTestTest1(8);CTest*Ptest2=newCTest(5);Test1.GetN();Ptest2-GetN();例11.16voidtime:copy(time&aa)if(this=&aa)return;*this=aa;voidmain()timea1,a2(12,0,0);a1.print();a1.copy(a2);a1.print();#includeiostream.hclasstimepublic:time()h=m=s=0;time(inti,intj,intk)h=i;m=j;s=k;voidcopy(time&aa);voidprint()couth“:”m:sendl;private:inth,m,s;注;由于静态成员函数可以不引用类对象而调用,所以没有包含对象地址的this指针。因此,如果要直接访问非静态数据成员,则编译器无法确定该数据成员属于哪个类(可通过向函数中传递对象来引用)。例11.11就是传递对象参数访问非静态数据成员。11.7 友元 friend友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但它可以访问类中的私有成员,友元可以是函数,也可以是类。注:提高了程序的运行效率,但破坏了封装性。一、友元函数#includeiostream.h#includemath.hclasssquarpublic:squar(doublexx,doubleyy)x=xx;y=yy;voidGetxy();private:doublex,y;voidsquar:Getxy()coutlength:x,wide:yendl;doublearea(squar&a)returna.x*a.y;/声明为友元才可引用声明为友元才可引用private成成员员voidmain()squarp(3.0,4.0);p.Getxy();doubled=area(p);coutTheareaisdendl;frienddoublearea(squar&a);二、友元类在类中声明另一个类为该类的友元类,声明前加friend。在友元类中可以引用该类中的private成员。#includeiostream.hclassXfriendclassY;public:voidSet(inti)x=i;voidDisplay()coutx,yendl;private:intx;staticinty;classYpublic:Y(inti,intj)a.x=i;X:y=j;voidDisplay();private:Xa;intX:y=1;voidY:Display()coutx=a.x,;couty=X:yendl;voidmain()Xb;b.Set(2);b.Display();/输出2,1Yc(6,9);c.Display();/输出x=6,y=9b.Display();/输出x=2,y=911.8 类的作用域:局部类 、嵌套类一、类的作用域类域:它是指在类的定义中由一对花括号所括起来的部份。注:类域中定义的变量不能使用auto, register,extern修饰符。只能用static修饰符,定义的函数也不能用extern.二、局部类和嵌套类1、局部类:在函数体内定义的类为局部类。Voidfun()classA注:局部类中不能说明静态成员函数,并且所有成员函数都必须定义在函数体内,很少用。2、嵌套类classApublic:classBprivate:三.对象的生存期1、局部对象:该对象所在函数块进入时,该对象被创建。该对象所在函数块退出时,该对象被释放。2、静态对象:程序第一次执行到所定义的对象时,该对象被创建。程序结束退出时,该对象被释放。3、全局对象;程序开始时,创建该对象。程序结束时,释放该对象。11.9 对象指针和对象引用一、成员的指针,对象的指针,对象的引用成员的指针指向数据成员的指针指向成员函数的指针定义一个指向类A中数据成员C的指针方法如下:数据类型名类名:*指针变量名=&类名:成员名intA:*pc=&A:c;定义一个指向类A中成员函数fun(int)的指针方法如下:数据类型名(类名:*指针变量名)(参数表列)=类名:成员函数名;int(A:*pfun)(int)=A:fun;对象的指针及对象的引用可直接用类名定义:类名*对象指针名或类名&对象引用名如如A*p=&x;A&m=x;#include“iostream.h“例11_19classApublic:A(inti)a=i;intfun(intb)returna*c+b;intc;private:inta;voidmain()Ax(8);intA:*PC;/定义一个指向数据成员的指针PC=&A:c;/指向成员C,相当于intA:*PC=&A:C;x.*PC=3;/用成员指针给成员赋初值,相当于xc=3int(A:*pfun)(int);/定义一个指向成员函数的指针pfun=A:fun;/int(A:*Pfun)(int)=A:funA*P=&x;/定义一个指向对象x的指针。cout*pfun)(5)funP-*pfun注:引用数据成员可以有下面几种形式:(P为指向对象x的指针,PC为指向对象成员C的指针)P-*PC;P-C;x.*PC;x.C;对成员函数的调用可以有下面几种形式(pfun为指向成员函数fun的指针)(P-*pfun)(5);P-fun(5);(x.*pfun)(5);x.fun(5);A&m=xm是对象x的引用。二、对象指针、二、对象指针、对象引用做函数参数对象引用做函数参数对象做函数参数,形参改变不影响实参对象指针、对象引用做函数参数,形参变化,实参也变化#includeiostream.hclassMpublic:M()x=y=0;M(inti,intj)x=i;y=j;voidcopy(M*m);voidaddxy(inti,intj)x+=i;y+=j;voidprint()coutx,yaddxy(10,20);m3.addxy(10,20);voidmain()Mp(5,7),q,r;q.copy(&p);q.print();r.copy(&p);r.print();fun(p,&q,r);p.print();q.print();r.print();classDATEpublic:DATE(intm,intd,inty);private:;DATEdate2=DATE(7,22,1998),DATE(7,23,1998);或:DATEdate2;date0=DATE(7,22,1998);date1=DATE(7,23,1998);初始化的方法有两种:定义的同时初始化;定义好之后,再单个元素初始化;11.10对象数组一、对象数组定义:声明:声明:类名数组名元素个数;访问方法:访问方法:通过下标访问数组名下标.成员名二、对象数组初始化n数组生成时,编译器调用数组中每个元素的构造函数或缺省构造函数,数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。n通过初始化列表赋值。例:DATEdate2=DATE(7,22,1998),DATE(7,23,1998);如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)。n不声明构造函数,则采用默认构造函数。n各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。n各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。n当数组中每一个元素对象被删除时,系统都要调用一次析构函数。n数组元素赋初值时,date0=DATE(7,22,1998);会有一个临时的对象产生,调用构造函数,初始化临时对象,之后将临时对象的值赋给date0,然后调用析构函数,释放掉临时对象。(3)用new生成的数组不能直接初始化(动态数组),需要每个元素单独初始化。DATE*date=newDATE2;date0=DATE(7,22,1998);date1=DATE(7,23,1998);销毁时要加上。Deletedate;如果省略了,则编译器只对第一个数组元素调用析构函数。#includeusingnamespacestd;classPointpublic:Point()X=Y=0;coutDefaultConstructorcalled.n;Point(intxx,intyy)X=xx;Y=yy;coutConstructorcalled.n;Point()coutDestructorcalled.n;intGetX()returnX;intGetY()returnY;voidMove(intx,inty)X=x;Y=y;private:intX,Y;intmain()Point*Ptr=newPoint2;/创建对象数组Ptr0.Move(5,10);/通过指针访问数组元素的成员Ptr1.Move(15,20);/通过指针访问数组元素的成员coutDeleting.endl;deletePtr;/删除整个对象数组return0;运行结果:运行结果:DefaultConstructorcalled.DefaultConstructorcalled.Deleting.Destructorcalled.Destructorcalled.#includeiostream.h#includestring.hclassscorepublic:score(char*s,doublen)strcpy(name,s);b=n;coutname.Constrn;score()coutDefaultn;score()coutname.Destrun;voidgetscore(char*s,double&n)strcpy(s,name);n=b;private:charname80;doubleb;voidmain()score*c2;doublen;chars80;scorec12=score(“c1-zhang”,98),score(“c1-l1”,87);/调用每个元素的构造函数调用每个元素的构造函数c2=newscore2;/调用默认的构造函数(调用默认的构造函数(2次)次)c20=score(“c2-ma”,97.5);/产生临时对象,调产生临时对象,调用构造函数,将临时对象的值给用构造函数,将临时对象的值给c20,调用析构调用析构函数,释放临时对象。函数,释放临时对象。c21=score(“c2-wang”,76.5);/同上同上for(inti=0;i2;i+)c1i.getscore(s,n);couts,nendl;for(i=0;i2;i+)c2i.getscore(s,n);couts,nendl;deletec2;题题1.分析以下程序执行的结果分析以下程序执行的结果#include#includeclassSamplepublic:intx,y;Sample()x=y=0;Sample(inta,intb)x=a;y=b;voiddisp()coutx=x,y=yendl;voidmain()Samples1(2,3);s1.disp();题题2.分析以下程序的执行结果分析以下程序的执行结果#includeclassSampleintx,y;public:Sample()x=y=0;Sample(inta,intb)x=a;y=b;Sample()if(x=y)coutx=yendl;elsecoutx!=yendl;voiddisp()coutx=x,y=yendl;voidmain()Samples1(2,3);s1.disp();一、阅读程序:一、阅读程序:题题3.分析以下程序的输出结果分析以下程序的输出结果#includeclassSampleintx;public:Sample(inta)x=a;coutconstructingobject:x=xendl;voidfunc(intn)staticSampleobj(n);voidmain()func(1);func(10);题题4分析以下程序的执行结果分析以下程序的执行结果#includeclassSampleintx,y;public:Sample()x=y=0;Sample(inta,intb)x=a;y=b;voiddisp()coutx=x,y=ydisp();题题1下面是一个类的测试程序,下面是一个类的测试程序,设计出能使用如下测试程序的类。设计出能使用如下测试程序的类。voidmain()Testa;a.init(68,55);a.print();其执行结果为:其执行结果为:测试结果:测试结果:68-55=13二、编程题二、编程题题题2生成一个生成一个savingsaccount类。用类。用static数据成员包含每个存款人的数据成员包含每个存款人的Annual(年利率),类成员包含一个(年利率),类成员包含一个private数据成员数据成员savings,表示当前存款表示当前存款额一个成员函数额一个成员函数calculate(),计算月利息,计算月利息,用用savings乘以乘以Annual除以除以12得到,并得到,并将这个月利息加进将这个月利息加进savings,用,用static成成员函数员函数modify(),将将Annual设置为新值。设置为新值。实例化两个不同实例化两个不同savingsaccount对象对象saver1和和saver2,结余分别为,结余分别为2000.00和和3000.00,将将Annual设置为设置为3%,计算每个计算每个存款人的月息并打印新的结果存款人的月息并打印新的结果,再将再将Annual设置为设置为4%,计算每个存款人的月计算每个存款人的月息并打印新的结果。息并打印新的结果。题题3.一圆型游泳池如图所示,现在需在其周围建一圆型过道,一圆型游泳池如图所示,现在需在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为并在其四周围上栅栏。栅栏价格为35元元/米,过道造价为米,过道造价为20元元/平方米。平方米。过道宽度为过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。和栅栏的造价。游泳池游泳池过道过道#includeconstfloatPI=3.14159;constfloatFencePrice=35;constfloatConcretePrice=20;/声明类声明类Circle及其数据和方法及其数据和方法classCircleprivate:floatradius;public:Circle(floatr);/构造函数构造函数floatCircumference()const;/圆周长圆周长floatArea()const;/圆面积圆面积;复习n现象、实体对象、类、对象(对象是类的实例)n抽象性、封装、继承与派生、多态性n数据成员、成员函数(两种定义方式)npublic、protected、private
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号