




已阅读5页,还剩172页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章对象和类,5.1类及其实例化5.2类和对象的性质5.3结构和联合5.4构造函数5.5析构函数5.6综合例题,5.7重载对象的赋值运算符5.8对象成员的初始化5.9类模板与标准模板库5.10面向对象编程的文件规范实验习题,本章重点介绍在C+中定义类、建立和使用对象的方法。虽然同类对象在其数据成员的取值方面是不相同的,但可以共用相同的代码。类是对同类对象的描述,它不但描述了对象之间的公有接口,同时也给出了对象的内部实现(数据成员和成员函数)。,像构造枚举和结构一样,类也是一种用户自己构造的数据类型并遵循C+的规定。例如,类也要先声明后使用;不管声明的内容是否相同,声明同一个名字的两个类是错误的,类是具有惟一标识符的实体;在类中声明的任何成员不能使用extern、auto和register关键字进行修饰;类中声明的变量属于该类,在某些情况下,变量可以被该类的不同实例所共享。,5.1类及其实例化5.1.1定义类,类和其他数据类型不同的是,组成这种类型的不仅可以有数据,而且可以有对数据进行操作的函数,它们分别叫做类的数据成员和类的成员函数,而且不能在类声明中对数据成员使用表达式进行初始化。,1.声明类类是对一组性质相同对象的程序描述。在C+中声明类的一般形式为class类名private:私有数据和函数public:公有数据和函数protected:保护数据和函数;,类声明以关键字class开始,其后跟类名。类所声明的内容用花括号括起来,右花括号后的分号作为类声明语句的结束标志。这一对花括号“”之间的内容称为类体。类中定义的数据和函数称为这个类的成员(数据成员和成员函数)。类成员均具有一个属性,叫做访问权限,通过它前面的关键字来定义。顾名思义,关键字private、public和protected以后的成员的访问权限分别是私有、公有和保护的,把这些成员分别叫做私有成员、公有成员和保护成员。访问权限用于控制对象的某个成员在程序中的可访问性,如果没有使用关键字,则所有成员默认声明为private权限。这些关键字的使用顺序和次数也都是任意的。,【例5.1】描述点的Point类。classPoint/类名Pointprivate:/声明为私有访问权限intx,y;/私有数据成员public:/声明为公有访问权限voidSetxy(inta,intb);/无返回值的公有成员函数voidMove(inta,intb);/无返回值的公有成员函数voidDisplay();/无返回值的公有成员函数intGetx();/返回值为int的公有成员函数intGety();/返回值为int的公有成员函数;/类声明以分号结束x和y是私有成员,Setxy、Display、Move、Getx和Gety是公有成员。因为只是声明函数,所以可只给出函数原型。【例5.2】是其等效的声明方式。,【例5.2】使用默认关键字及改变关键字顺序和次数的Point类。classPoint/类名Pointintx;/默认私有数据成员public:/声明为公有访问权限/无返回值的公有成员函数Setxy的函数原型voidSetxy(int,int);/无返回值的公有成员函数Move的函数原型voidMove(int,int);voidDisplay();/无返回值的公有成员函数的函数原型intGetx();/返回值为int的公有成员函数的函数原型intGety();/返回值为int的公有成员函数的函数原型,private:/声明为私有访问权限inty;/私有数据成员;/类定义以分号结束由此可见,成员函数声明的规则与第4章所述的函数声明规则相同。2.定义成员函数类中说明的成员函数用来对数据成员进行操作。例如,Point类的Setxy函数用来为该类的对象设置初始值,而当调用成员函数Getx时,则返回一个对象的数据成员x的值。在类中只对这些成员函数进行了函数声明,还必须在程序中实现这些成员函数。定义成员函数的一般形式为,返回类型类名成员函数名(参数列表)成员函数的函数体/内部实现其中“”是作用域运算符,“类名”是成员函数所属类的名字,“”用于表明其后的成员函数是属于这个特定的类。换言之,“类名成员函数名”的意思就是对属于“类名”的成员函数进行定义,而“返回类型”则是这个成员函数返回值的类型。余下的工作就是定义成员函数的函数体。例如Setxy是类Point的成员函数,它没有返回值,则定义如下:voidPointSetxy(inta,intb)x=a;y=b;,将“voidPointSetxy(inta,intb)”理解为定义Point的函数成员Setxy(inta,intb),该成员带有两个整型参数,函数没有返回值(void)。按此方法,可写出其他几个成员函数的定义:voidPointMove(inta,intb)x=x+a;y=y+b;voidPointDisplay()coutx,yendl;intPointGetx()returnx;intPointGety()returny;,也可以使用关键字inline将成员函数定义为内联函数,例如:inlineintPointGetx()returnx;如果在声明类的同时,在类体内给出成员函数的定义,则默认为内联函数。例如在类中将声明Getx的语句“intGetx();”改为“intGetx()returnx;”,则Getx为内联函数。一般直接在类体内给出简单成员函数的定义。有些成员函数的实现方式不止一种,例如voidPointDisplay()coutGetx(),Gety()Display();/显示指针p所指对象A的数据/成员A.x和A.y之值,【例5.3】根据上面对Point类的定义,演示使用Point类的对象。voidprint(Pointa)/使用Point的对象a作为函数参数a.Display();/显示对象a的数据成员的值voidmain()PointA,B;/声明对象A.Setxy(25,55);/为对象A赋初值B=A;/B的数据成员取A的数据成员之值A.Display();/显示A的数据成员A.Move(-10,20);/移动Aprint(A);/等价于A.Display();print(B);/等价于B.Display(),coutA.Getx()endl;/只能使用A.Getx(),不能使用A.x本例中的print函数使用Point的对象作为参数。C+推荐使用下面的引用的形式:voidprint(Pointfor(inti=0;i2;i+)deleteptri;,必须为每个数组元素调用delete析构相应的对象数组元素所占的空间。输出结果如下:Initializing2,4Initializing7,8DestructorisactiveDestructorisactive数据成员使用指针时,需要为它们申请空间,要注意区分动态对象数组和指针数组的区别,以及析构时的异同。注意分析下面的例子。,#includeusingnamespacestd;classTestprivate:char*str;public:Test(char*str=Here!);Test();;,Test:Test(char*s)/定义构造函数str=newcharstrlen(s)+1;strcpy(str,s);coutInitializingstrendl;Test:Test()coutDeletestrendl;,voidmain()Test*ptr1=newTest(Wearehere!);Test*ptr2=newTest2;Test*ptr32=newTest(We),newTest(You);deleteptr1;deleteptr2;for(inti=0;i2;i+)deleteptr3i;,第一个创建的是一个Test对象的指针ptr1,初始化的值为:Wearehere!析构需要使用一个单独的语句“deleteptr1;”。第二个创建的是一个Test对象的动态数组,这个数组有两个元素,初始化的值在内中顺序排列为:Here!0Here!0指针ptr2指向首地址,需要使用语句deleteptr2;来析构这分配给这两个对象的存储空间。第三个创建的是一个Test对象的动态数组,这个数组有两个元素,初始化的值分别为:对象,ptr30内容为We;ptr31内容为You。所以要使用循环语句逐个析构。输出结果如下:InitializingWearehere!InitializingHere!InitializingHere!InitializingWeInitializingYouDeleteWearehere!DeleteHere!DeleteHere!DeleteWeDeleteYou,如果在定义类时没有定义析构函数,C+编译器要为这个类产生一个默认的析构函数。正如没有给类定义构造函数,C+编译系统会产生一个默认构造函数一样,编译器也为它产生一个函数体为空的默认析构函数:TestTest(),5.5.3默认析构函数,【例5.9】演示调用构造函数及析构函数的综合例题。下面这个程序综合演示了调用构造函数、调用复制构造函数及调用析构函数的顺序,并演示了调用复制构造函数的3种不同情况。另外,本程序还比较了函数使用对象和对象的引用为参数的情况。,5.6综合例题,#includeusingnamespacestd;classPointprivate:intX,Y;public:Point(inta=0,intb=0)/构造函数X=a;Y=b;coutInitializingendl;Point(constPoint以绕过类型转换所带来的运行时间开销。程序运行结果如下:WeTheyWeWeWeGohome!,可以在一个类中说明具有某个类的类型数据成员,这些成员称为对象成员。在类X中说明对象成员的一般形式为:classX类名1成员名1;类名2成员名2;类名n成员名n;,5.8对象成员的初始化,说明对象成员是在类名之后给出对象成员的名字。为初始化对象成员,X类的构造函数要调用这些对象成员所在类的构造函数,X类的构造函数的定义形式如下:XX(参数表0):成员1(参数表1),成员2(参数表2),成员n(参数表n)/其他操作冒号“:”后由逗号隔开的项组成成员初始化列表,其中的参数表给出为调用相应成员所在类的构造函数时应提供的参数。这些参数一般来自“参数表0”,可以使用任意复杂的表达式,其中可以有函数调用。如果某项的参数表为空,则表中相应的项可以省略。,对象成员构造函数的调用顺序取决于这些对象成员在类中说明的顺序,与它们在成员初始化列表中给出的顺序无关。当建立X类的对象时,先调用对象成员的构造函数,初始化对象成员,然后才执行X类的构造函数,初始化X类中的其他成员。析构函数的调用顺序与构造函数正好相反。下面分析一个实际的例子。,【例5.11】分析下面程序中析构函数与构造函数的调用顺序。#includeusingnamespacestd;classobjectprivate:intval;public:object():val(0)coutDefaultconstructorforobject“endl;object(inti):val(i)coutConstructorforobject“valendl;object()coutDestructorforobject“valendl;,classcontainerprivate:objectone;/初始化顺序与对象成员产生的顺序有关objecttwo;/排在前面的先初始化intdata;public:container():data(0)coutDefaultconstructorforcontainer“endl;container(inti,intj,intk);container()coutDestructorforcontainer“dataendl;,/与初始化列表顺序无关containercontainer(inti,intj,intk):two(i),one(j)data=k;coutConstructorforcontainer“datab)?a:b;public:Max4(T,T,T,T);TMax(void);template/定义成员函数必须再次声明模板Max4Max4(Tx1,Tx2,Tx3,Tx4):a(x1),b(x2),c(x3),d(x4)template/定义成员函数必须再次声明模板,TMax4Max(void)/定义时要将TMax4看做整体returnMax(Max(a,b),Max(c,d);voidmain()Max4C(W,w,a,A);/比较字符Max4A(-25,-67,-66,-256);/比较整数Max4B(1.25,4.3,-8.6,3.5);/比较双精度实数coutC.Max()A.Max()B.Max()endl;输出结果为:w-254.3,【例5.13】演示对4个数字求和的类模板程序。#includeusingnamespacestd;template/可以传递程序中的整数参数值classSumprivate:Tmsize;/数据成员public:Sum(Ta,Tb,Tc,Td)/构造函数m0=a;m1=b;m2=c;m3=d;TS()/求和成员函数returnm0+m1+m2+m3;,voidmain()Sumnum1(-23,5,8,-2);/整数求和/单精度求和。使用f显式说明float型Sumf1(3.5f,-8.5f,8.8f,9.7f);Sumd1(355.4,253.8,456.7,-67.8);/字符减,等效于W-4,结果为SSumc1(W,-2,-1,-1);coutnum1.S(),f1.S(),d1.S(),c1.S()endl;输出结果为:-12,13.5,998.1,S,标准模板库(standardtempletelibrary,STL)是美国国家标准委员会(americannationalstandardsinstitute,ANSI)和国际标准化组织(internationalstandardizationorganization,ISO)于1988年3月制定的标准。它主要由两种组件构成:一种是容器(container),包括vector,list,set,map,stack,和deque等类。另一种是用以操作这些容器类的所谓泛型算法(genericalgorithm),包括find(),sort(),replace()和merge()等。,5.9.2标准模板库STL,泛型算法提供了许多可用于容器类的操作行为,而这些算法和想要操作的元素类型无关。正如第3章3.4节所述,它们和int,double或者string全然无关。其实,它们与容器类型也彼此独立(不论容器是vector或list)。泛型算法系通过functiontemplate技术,达到“与操作对象的数据类型相互独立”的目的。而达到“与容器无关”的诀窍,就是不直接在容器身上进行操作。这是借助一对iterators(迭代子)实现,它是一种泛型指针。,在3.4节介绍了指示第一元素的begin(),其实还有指示结束的标记end()(最后一个元素后面的结束标记),它们标示要进行迭代的元素空间。如果begin不等于end,算法便会首先作用于begin所指元素,并将begin前进一个位置,然后作用于当前的begin所指元素身上,如此继续进行,直到begin等于end为止。图5.3是向量a和迭代子示意图。图中的rbegin和rend是提供给逆向泛型指针的开始和结束标志。逆向泛型指针的加操作是使它向rend方向移动,减操作向rbegin移动。它的定义方法如下:vectorreverse_iterator指针名;/reverse_iterator是逆向泛型指针关键字,图5.3迭代子示意图,【例5.14】演示双向访问的例子。#include#includeusingnamespacestd;voidmain()charst11=abcdefghij;vectora(st,st+10);vectoriteratorp=a.begin();/定义正向泛型指针/并初始化vectorreverse_iteratorps;/定义逆向泛型指针for(p=a.begin();p!=a.end();+p)/正向访问cout*p;/输出abcdefghijcoutendl;,for(p=a.end()-1;p!=a.begin()-1;-p)/使用正向泛型/指针逆向访问cout*p;/输出jihgfedcbacoutendl;for(ps=a.rbegin();ps!=a.rend();+ps)/使用逆向泛型/指针正向访问,使用+运算cout*ps;/输出jihgfedcbacoutendl;for(-ps;ps!=a.rbegin()-1;-ps)/使用逆向泛型指针/逆向访问,使用-运算cout*ps;/输出abcdefghij,STL是用在标准数据结构和标准算法领域中的一种模板库,它所提供的每一个组件都具有很强的通用性。它作为一种基本组成单元,用来生成更高层的类、类库和应用程序框架;也可作为软件的构件或粘合剂,把复杂的面向对象的结构集成为一个整体。因为它的每一个组件都以模板的方式实现,所以C+模板的技术和工作机理是深入了解STL的敲门砖。由于篇幅关系,仅作简单介绍。STL现在有75个泛型算法,下面给出最常用的算法。,搜寻类算法:find()adjacent_find()count()find_if()count_if()binary_search()find_first_of()排序及次序整理类算法:merge()partial_sort()random_shuffle()partition()reverse()rotate()sort()复制、删除和替换类算法:copy()remove()remove_if()replace()replace_if()swap()unique()关系算法:equal()includes()mismatch(),生成与变更算法:fill()for_earch()generate()transform()数值类算法:accmulate()adjacent_difference()partial_sum()inner_product()集合类算法:set_union()set_difference()第3章介绍了vector的操作,其实也是容器类的共同操作。它们还有如下共同操作:equality(=)和inequality(!=)运算符,返回true或false。assignment(=)运算符,将某个容器复制给另一个容器。,【例5.15】演示使用泛型算法及插入操作的例子。#include#include#include/泛型算法头文件usingnamespacestd;voidmain()charst111=abcdefghij,st26=LMNOP;vectora(st1,st1+10);vectorb(st2,st2+5);typedefvectoriteratoriterator;iteratorp;/寻找内容为h的元素作为插入位置iteratorit=find(a.begin(),a.end(),h);,a.insert(it,b.begin(),b.end();/将另一个向量元素插入for(p=a.begin();p!=a.end();p+)/输出新内容cout*p;coutendl;it=find(a.begin(),a.end(),h);/查找h的新位置/查找向量自己元素L的位置iterators1=find(a.begin(),a.end(),L);/查找向量自己元素P的位置iterators2=find(a.begin(),a.end(),P);a.insert(it,s1,s2+1);/复制自己的元素for(p=a.begin();p!=a.end();p+)cout*p;coutendl;it=find(a.begin(),a.end(),c);/查找c的位置s1=b.begin();,s2=find(b.begin(),b.end(),P);/查找P的位置a.insert(it,s1,s2);/复制向量b的元素for(p=a.begin();p!=a.end();p+)cout*p(b)?(a):(b)因宏定义有许多不安全因素,对需要使用无参数宏的场合,应该尽量使用const代替宏定义。对使用有参数宏的场合,可用内联函数达到同样的效果,所以不再过多介绍。在程序的一个地方定义的宏名,如果不想使其影响到程序的其他地方,可以在不再使用时用#undef删除。,3.条件编译指令条件编译指令是#if,#else,#elif和#endif,它们构成类似于C+的if选择结构,其中#endif表示一条指令结束。编译指令#if用于控制编译器对源程序的某部分有选择地进行编译。该部分从#if开始,到#endif结束。如果#if后的常量表达值为真,则编译这部分,否则就不编译该部分,这时,这部分代码相当于从源文件中删除掉。编译指令#else在#if测试失效的情况下建立另外一种选择。例如可以在#else分支中使用编译指令#error输出出错信息。,#error使用的形式为:#error出错信息“出错信息”是一个字符序列。当遇到#error指令时,编译器显示其后面的“出错信息”,并中止对程序的编译。编译指令可嵌套,嵌套规则和编译器对其处理的方式与C+的if语句嵌套情况类似。,4.defined操作符关键字defined不是指令,而是一个预处理操作符。用于判定一个标识符是否已经被#define定义。如果标识符identifier已被#define定义,则defined(identifier)为真,否则为假。例如,在设计程序时,为便于检查程序中的错误,要使用输出语句显示一些调试信息,在程序正式交付使用之后,不应再显示这些信息,程序可以用下面的方式设计:,#includeusingnamespacestd;#defineDEBUGvoidmain()inti=555;/程序#ifdefined(DEBUG)coutDEBUG:valueofiisi;#endif,在程序调试完后,删除程序中的#define(DEBUG)指令,重新编译程序即可,程序的其他地方无需改动。另外,C+提供#ifndef和#ifdef只是为兼容C程序,今后可能被淘汰,因此建议程序员使用defined进行标识符的测试。条件编译指令#ifdef和#ifndef用于测试其后的标识符是否被#define定义,如果已经被定义,则#ifdef测试为真,#ifndef测试为假;如果没有被定义,则#ifdef测试为假,#ifndef测试为真。下面两种方式是完全等效的:,#if!defined(HEAD_H)#defineHEAD_H/程序定义部分#endif#indefHEAD_H#defineHEAD_H/程序定义部分#endif使用defined而不使用#ifdef或#ifndef测试一个标识符是否被定义的好处在于,可以用逻辑运算组成复杂的测试表达式。例如:defined(LARGE)&!defined(SMALL)!defined(TINY),在多文件设计中,由于文件包含指令可以嵌套使用,可能会出现不同文件包含了同一个头文件,这会引起变量及类的重复定义。为了避免重复编译类的同一个头文件,可对这
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026届湖南明德中学高三化学第一学期期中复习检测模拟试题含解析
- 2025年二季度骨科护理技术操作常见并发症理论考试题及答案
- 2025年保健品考试题及答案
- 2026届辽宁省本溪中学化学高三上期末质量检测模拟试题含解析
- 2025年陪诊师模拟考试题库及答案
- 2025年环保保护试题及答案
- 2025年注册验船师资格考试(C级船舶检验专业能力)模拟试题及答案二
- 2025年高级运动营养师实操技能解析与模拟题
- 2025年人力资源管理师专业技能测试题库
- 桃花源记app课件
- 2025-2026学年统编版(2024)初中语文七年级上册教学计划及进度表
- 标准化产品需求文档编写方法
- 办公室文秘岗试题带答案
- 2025年河南疾控中心考试题库
- 2025年高考【数学】真题及答案(新高考Ⅱ卷)
- 养老护理员竞赛理论试卷答案(含答案)
- 2025年广东省广州市中考历史试卷(含解析)
- 2025年四川省能源投资集团有限责任公司人员招聘笔试备考题库及答案详解(新)
- 广东省公路服务区管理系统升级及运维项目
- 2025版《中国系统性红斑狼疮诊疗指南》解读 4
- 造林后续管理办法
评论
0/150
提交评论