版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第3章 面向对象程序设计3.1 面向对象程序设计思想3.1.1 历史回顾1967年挪威计算中心的Kisten Nygaard和Ole Johan Dahl开发了Simula67语言,它提供了比子程序更高一级的抽象和封装,引入了数据抽象和类的概念,它被认为是第一个面向对象语言。20世纪70年代初,Palo Alto研究中心的Alan Kay所在的研究小组开发出Smalltalk语言,之后又开发出Smalltalk-80,Smalltalk-80被认为是最纯正的面向对象语言,它对后来出现的面向对象语言,如Object-C,C+,Self,Eiffl都产生了深远的影响。随着面向对象语言的出现,面向对
2、象程序设计也就应运而生且得到迅速发展。之后,面向对象不断向其他阶段渗透,1980年Grady Booch提出了面向对象设计的概念,之后面向对象分析开始。1985年,第一个商用面向对象数据库问世。1990年以来,面向对象分析、测试、度量和管理等研究都得到长足发展。实际上,“对象”和“对象的属性”这样的概念可以追溯到20世纪50年代初,它们首先出现于关于人工智能的早期著作中。但是出现了面向对象语言之后,面向对象思想才得到了迅速的发展。过去的几十年中,程序设计语言对抽象机制的支持程度不断提高:从机器语言到汇编语言,到高级语言,直到面向对象语言。汇编语言出现后,程序员就避免了直接使用0-1,而是利用符
3、号来表示机器指令,从而更方便地编写程序;当程序规模继续增长的时候,出现了Fortran、C、Pascal等高级语言,这些高级语言使得编写复杂的程序变得容易,程序员们可以更好地对付日益增加的复杂性。但是,如果软件系统达到一定规模,即使应用结构化程序设计方法,局势仍将变得不可控制。作为一种降低复杂性的工具,面向对象语言产生了,面向对象程序设计也随之产生。3.1.2 什么是面向对象程序设计首先我们要了解的是,什么是面向对象的程序设计方法OOP(Object Oriented Programming),它与传统的结构化程序设计方法SP(Structure Programming)的主要区别是什么。要弄
4、清什么是OOP,则应了解和回顾一下传统的结构化程序设计方法及其设计思想、程序结构与特点,以便比较对照二者之间的差异,由此领会OOP对SP的继承、丰富、发展和突破。1结构化程序设计思想结构化程序设计SP是60年代诞生的,以针对当时爆发的所谓“软件危机”的挑战,而在70、80年代遍及全球成为所有软件开发设计领域、每个程序员都广为采用的传统的结构化程序设计方法与技术的简称。它的产生和发展形成了现代软件工程学的基础。SP的总的设计思路是两点:一是自顶向下,层次化;二是逐步求精,精细化。其程序结构是按功能划分基本模块为树型结构,使模块间的关系尽可能简单、独立,并从而单独验证模块的正确性。每一模块均由顺序
5、、选择和循环三种基本结构组合而成。综言之,此即所谓“模块化”。因此,SP的程序的基本特点是:a按层次组织模块。(即战略上划分战役)b每一模块只有一个入口,一个出口。(每一战役尽可能简单、明确)c代码和数据分离实现。(战术上程序=数据结构+算法)SP实现上述战略战术的具体方法和技术是:使用局部变量和子程序。SP的优点可以概括为:a子程序对程序其它部分没有或尽可能少的连带作用,从而为共享代码奠定基础。b由于SP方法的模块化分解与功能抽象,自顶向下,分而治之的手段及技术,从而能有效地将一个复杂的、中大规模的程序系统的设计任务分成许多易于控制和处理、可独立编程的子任务、子程序模块。c对于上述子程序或模
6、块中的每一个都有一个清晰的抽象界面,它只说明:对应用程序设计者来说只需了解它做什么(What to do),而不必说明:它如何去做(How to do)。但从本质上说,由Pascal和C这样的程序设计推动的传统的SP仍是一种面向数据和过程的设计方法,它把数据和过程分离为相互独立的实体,用数据代表问题空间中的客体借以表达实际问题中的信息;程序代码则体现用于处理加工这些数据的算法。于是,程序员在编程时必须时刻考虑要处理的数据结构和类型,对不同的数据格式(结构和类型)即使要作同样的处理计算,或者对于相同的数据格式要作不同的处理都要编写不同的程序,可见,使用传统SP方法设计出来的程序或系统,其可重用的
7、成分很少。另一方面,当把数据和代码作为不同的分离体时,总存在着用错误的数据调用正确的程序,或用正确的数据调用错误的程序的危险。因而,使数据与程序始终保持相容一致,已成为程序员的一个沉重的负担。这就是为什么在开发一个大型软件的过程中,如果用户在工程的中、后期对数据格式或实现方案提出任何改变请求时,变化摧毁了前面工作的一切,前功尽弃。2面向对象程序设计思想为了克服和解决当今许多大型软件项目和系统设计都接近或达到了SP方法所难以控制处理和适应其变化的上述种种矛盾及问题而产生了OOP方法与技术。OOP吸取了SP的一切优点和长处,同时又正视和顺应现实世界由物质和意识两部分组成的这一普遍原理,将其映射到对
8、象的解空间就是:具体事物对象,抽象概念类;而一个对象无非就是这样一个实体,它具有一个名字标识,并带有自身的状态(属性)和自身的功能(行为或方法)。世界上的所有事物(对象)就是如此奇妙地简单。这正是面向对象方法和技术所追求的目标:将世界上的问题尽可能简单化。事实上,用计算机求解的问题都是现实世界中的问题,它们无非都是由一些相互联系的,并处于不断运动变化的事物(即对象)所组成。每个具体对象都可用两个特征来把握:描述事物静态数据和可施于这些数据上的有限操作。也就是说,应当把数据结构和对数据的操作放在地起,作为一个相互依存不可分离的整体(即对象类)来处理(封装、信息隐蔽、数据抽象等)。并且要考虑不同事
9、物即对象类之间的联系(消息、通讯、协议等)和事物即对象类的重用性和变化性(继承、多态等),这才更符合客观世界的本来面目。概言之,OOP与SP不同,它除了包纳了SP的一切优特点与机制外,同时又是一个引入若干强有力的、更能反映事物本质的新概念、新机制,从而开创了一个程序新天地的强大新方法。OOP的基本原理是:用问题领域的模型来模拟现实世界,从而设计出尽可能直接、自然地表示问题求解方法的软件,这样的软件系统由对象组成,而对象则是完整反映客观世界事物具有不可分割的静态属性(数据结构)与动态行为(方法)的,并且它们是既有联系又有变化发展的实体。OOP方法所涉及的新概念,在理论上最具一般性的对应物及其描述
10、如下等等:a对象对客观世界事物的表示或描述,世界上任何具体事物均可称为对象。b类是一组对象的抽象定义(抽象概念、抽象数据类型)c方法对应于对象的功能。d消息对象之间通讯的途径。e继承对象间具有相同性和差异变化的关系。基于以上概念,面向对象就成了一个作为现实世界映射的封闭的缩微世界,一个对象具有自身的属性(私有数据成员)和可以为自己或别人工作的功能(方法、操作或称成员函数),它能通过发送消息与其它对象进行通讯,协同完成任务。OOP并不是用一两句话就可以精确定义描述清楚的一套方法与技术,然而在这里,我们还是非形式化地、笼统地说明和描述一下OOP的概念:OOP是SP、信息掩蔽、知识表示、并行处理领域
11、等概念的继承和发展。OOP的特点是把系统设计成将所需要求解的问题分解成一些对象及对象间传递消息的符合客观世界规律的自然过程。OOP方法使程序员摆脱具体的数据格式和过程的束缚,将精力集中到对要处理的对象的设计和研究上,从而大大减少了软件开发的复杂度。OOP包括了功能抽象的抽象数据、信息隐蔽即封装等机理,使对象的内部实现与外界隔离,从而提供了更理想的模块化机制,大大减少了程序间的相互干扰和副作用。OOP的抽象数据类型对象类及继承,则为我们提供了理想的高可重用性的软件成份和机制。此外,在人工智能领域中,若用OOP方法表示知识,则更接近于自然的客观世界的知识表示和认识论,因而不仅能表达描述非常复杂的客
12、观事物和知识,而且具有模块性强、结构化程度高,便于分层实现,有利于实际开发和维护。因此OOP方法和技术的优特点将非常适合知识处理、知识库、专家系统等人工智能领域和数据库、CAD、图形处理(多媒体)、系统模拟与构造等大型复杂软件工程化开发。3.1.3 面向对象程序设计语言一个语言要称为面向对象语言必须支持几个主要面向对象的概念。根据支持程度的不同,通常所说的面向对象语言可以分成两类:基于对象的语言,面向对象的语言。基于对象的语言仅支持类和对象,而面向对象的语言支持的概念包括:类与对象、继承、多态。举例来说,Ada就是一个典型的基于对象的语言,因为它不支持继承、多态,此外其他基于对象的语言还有Al
13、phard、CLU、Euclid、Modula。面向对象的语言中一部分是新发明的语言,如Smalltalk、Java,这些语言本身往往吸取了其他语言的精华,而又尽量剔除他们的不足,因此面向对象的特征特别明显,充满了蓬勃的生机;另外一些则是对现有的语言进行改造,增加面向对象的特征演化而来的。如由Pascal发展而来的Object Pascal,由C发展而来的Objective-C,C+,由Ada发展而来的Ada 95等,这些语言保留着对原有语言的兼容,并不是纯粹的面向对象语言,但由于其前身往往是有一定影响的语言,因此这些语言依然宝刀不老,在程序设计语言中占有十分重要的地位。C+语言及环境中的OO
14、P有五个最基本概念,这五个基本概念术语是构成整个C+面向对象方法与技术的柱石。它们是:a对象(Object)b消息(Message)c方法(Method)d类(Class)e继承(Inheritence)下面我们将分别介绍它们的含义、组成和性质。1对象什么是对象?对象是私有数据及可以对这些数据施加的操作结合在一起所构成的独立实体。即一个对象基本上包括两个主要部分:记载对象内部状态的数据和表现对象活动与功能的程序。从传统的SP观点来看,数据和处理它们的代码是两个不同的独立实体,它们之间的正确联系、选择和匹配需要应用系统的设计者时刻考虑。而OOP中,一个对象则是由私有数据和其上的一组操作代码组成的
15、一个统一体。请看如下示意图:SP观点OOP观点数据代码数据代码对象对象的动作取决于发送给该对象的消息表达式,消息告诉对象要求完成的功能,并激活该功能,这意味着对象具有自动“知道”如何完成相应操作代码的“智能”选择机制,正是这一机制把SP中应用系统程序员作出的选择操作数据与相应操作函数代码匹配的负担转移给了系统设计者。上述所谓的“智能化”的选择机制并不神秘,只不过是在对象的操作(过程或函数)表中对消息操作名进行按名搜索而已,对应C+来说,执行一个消息表达式就是触发选择机制在相应对象的调度表(内存控制块)中进行搜索,找到后便转向对应于该消息的成员函数代码中运行。事实上,除了上述选择匹配机制是新东西
16、外,下面这些术语的含义和传统的SP相应名词及说明是相以对应的:对象内存中的数据块对象标识符指向上述数据的指针向对象发送消息使成员函数作用于数据在OOP中,数据和代码是紧密结合在一起的计算机中的一内存分块。但局部于该对象中的数据只可以由对象中的代码块(成员函数)来访问,即数据是对象私有的(受保护的),不能被其它对象影响和修改。简单地说,一个对象从不关心其它对象的内部细节,而只关心它自己,对象之间的通讯只是以“做什么”的消息发送为契机,并且认为接受消息的对象是知道如何去做的。对象可以非常简单,也可以十分复杂。通常复杂对象由简单的对象层层嵌套组合而成。今后我们将从C+程序实例中看到,对象形式上只是系
17、统程序员、应用程序员或用户定义的“类”这种数据结构类型的变量,当我们定义一个对象,就已创造出了一种新的抽象数据类型。对象可以说是构成和支撑整个OOP最重要的细胞与基石。除了上述基本组成、关系和机理特性外,对象还有以下性质和优点:a模块独立性。逻辑上看,一个对象是独立存在的模块,从外部看这模块,只需了解它具有哪些功能,至于它如何实现这些功能和使用哪些局部数据来完成它们的细节则“隐蔽”在模块内部。这意味着模块内部状态不受外界干扰改变,也不会殃及到其它模块。即模块间依赖性极小或几乎没有;各模块可独立为系统组合选用,也可被程序员重用,而不必担心波及或破坏别的模块。b动态连接性。客观世界中各式各样的对象
18、,并不是孤立存在的,正是它们之间的相互作用、联系和连接,才构成了世间各种不同的系统。同时,在OOP中,通过消息(传送)激活机制,把对象之间动态联系连接起来,便称为对象的动态连接性。c易维护性。由于对象的功能被“隐蔽”和好象被一层封装壳保护在对象内部,故无论是修改,还是功能完善以及实现的细节都被囿于该对象内部,不会播及到外部去,这增加了对象和整个系统的易维护性。2消息什么是消息?消息是要求某个对象执行其中某个功能操作的规格说明。通俗地讲,OOP中的术语“消息”只不过是现实世界中的“请求”、“命令”等日常生活用语的同义词。通常,OOP的一条消息由选择器(“消息操作”或“消息名”)及若干变元(0个或
19、多个实参)和接受消息的对象三个部分组成。从程序设计语言的角度出发来说,除前面曾经提到过的消息具备提供(激活)对象的动态选择机制外,消息表达式从形式上看类似于普通的函数调用。如代码:switch(message) case WM_LBUTTONDOWN;/鼠标左按钮被按下 . break; case WM_LBUTTONUP;/ 鼠标左按钮被放开 . break;一般,我们把发送消息的对象称为发送者,接收消息的对象称为接收者。对象间的联系,只能通过传送消息即执行消息表达式来进行。对象也只有在收到消息时,才被激活,补激活的对象的代码将知道如何去进行操作它的私有数据,以完成所发送的消息要求的功能。消
20、息发送不考虑具体的接收者,对象可以响应,也可以不响应。3方法什么是方法?OOP中的术语“方法”对应于对象的能力,即它是实现对象所具有的功能操作的代码段,是响应消息的“方法”。C+中,方法即是类中定义的成员函数,它只不过是该类对象所能执行的操作的算法实现而已。方法与消息是对应的,每当对象收到一个消息,它除了能用“智能化”的选择机制知道和决定应该去做什么(What to do)外,还要知道和决定该怎样做(How to do)。而方法正是与对象相连决定怎么做的的操作执行代码。4类什么是类?类是对一组对象的抽象归纳概括,确切地说,类是对一组具有相同数据成员和相同操作成员的对象的定义和说明。而每个对象都
21、是某个类的一个具体实例。在OOP中,每个对象由一个类来定义或说明,类可以看作生产具有相同属性和行为方式对象的模板。类或分类就是所谓“物以类聚,人以群分”的意思,“类”就是具有相似性质的事物的同类特征的集中。简言之,按照对象的相似性,我们把对象分成一些类和子类,故相似对象的集合即称为类。对C+程序员而言,类实际上是一种对象类型。它描述属于该类型的具有相同结构和功能的对象的一般性质。而结构类型则是只有将相关信息(仅只是不同类型的数据集)集中在一起的一个C的变量集。从理论上讲,类(class)是一组同类对象的抽象,它将该种对象的共同特征,包括操作特征和存储特征都集中封装起来。由属于此类的对象共享。例
22、如,讨论食肉动物时,我们并不只是特指老虎和狮子,而只是抽象出它们的共同特征:比如喜欢吃肉。更具体地,又如:若为屏幕上的点设计一个类如下:class point x,y; new(); delete(); move(); highlight();则可以说明point类的对象如:point a(100,50),b(30,60);piont中定义的数据是抽象数据类型,而对象a,b中的数据则是对象对应着内存中的具体数据单元拷贝块,以其初始化时的具体值和存储于不同单元块而区分不同的对象。5继承什么是继承?继承是对象类间的一种相关关系,并具有以下关键内容:a类间的共享的特征(包括共享程序代码和数据)。b类
23、间的细微区别或新增部分。c类间的层次结构。继承机制既是一个对象类就获得另一个对象类特征的过程,也是一个以分层结构组织、构造和重用类的工具。它是解决客观对象“相似又不同”的方法。具体地讲,若类B继承了类A,则属于类B中的对象便具有类A的一切性质和功能。我们称A为基类或父类,而称B为A的派生类或子类。因此,要描述或构造一个新类B,只须去继承一个与之有共同特征的父类A,然后描述与父类不同的少量特征(即增加一些新的数据成员或成员函数)即可。继承机制有以下几个优特点:a能清晰体现相似类间的层次结构关系。b能减少代码和数据的重复冗余度,增强程序的重用性。c能通过增强一致性来减少模块间的接口和界面,增强程序
24、的易维护性。d继承是能自动传播代码的有力工具。e继承还是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。如果没有继承概念的支持,则OOP中所有的类就象一盘各自为政、彼此独立的散沙,每次软件开发都要从“一无所有”开始。C+中,除了一般OOP方法的树型层次结构的单一继承外,还支持多重继承。对单一继承,每个子类只能有一个父类,而对多重继承,每个子类则可以有多个父类。3.1.4 面向对象程序设计的优点面向对象出现以前,结构化程序设计是程序设计的主流,结构化程序设计又称为面向过程的程序设计。在面向过程程序设计中,问题被看作一系列需要完成的任务,函数(在此泛指例程、函数、过程)用于完成这些任
25、务,解决问题的焦点集中于函数。其中函数是面向过程的,即它关注如何根据规定的条件完成指定的任务。在多函数程序中,许多重要的数据被放置在全局数据区,这样它们可以被所有的函数访问。每个函数都可以具有它们自己的局部数据。下图显示了一个面向过程程序中函数和数据的关系。代码数据这种结构很容易造成全局数据在无意中被其他函数改动,因而程序的正确性不易保证。面向对象程序设计的出发点之一就是弥补面向过程程序设计中的一些缺点:对象是程序的基本元素,它将数据和操作紧密地连结在一起,并保护数据不会被外界的函数意外地改变。下图显示了一个面向对象程序中对象与函数和数据的关系。代码数据代码比较面向对象程序设计和面向过程程序设
26、计,还可以得到面向对象程序设计的其他优点: 数据抽象的概念可以在保持外部接口不变的情况下改变内部实现,从而减少甚至避免对外界的干扰; 通过继承大幅减少冗余的代码,并可以方便地扩展现有代码,提高编码效率,也减低了出错概率,降低软件维护的难度; 结合面向对象分析、面向对象设计,允许将问题域中的对象直接映射到程序中,减少软件开发过程中中间环节的转换过程; 通过对对象的辨别、划分可以将软件系统分割为若干相对为独立的部分,在一定程度上更便于控制软件复杂度; 以对象为中心的设计可以帮助开发人员从静态(属性)和动态(方法)两个方面把握问题,从而更好地实现系统; 通过对象的聚合、联合可以在保证封装与抽象的原则
27、下实现对象在内在结构以及外在功能上的扩充,从而实现对象由低到高的升级。 3.1.5 面向对象程序设计主要特征面向对象程序设计主要特征有三个:继承性、封装性和多态性。1继承性继承性在上面已作介绍,不再重复。2封装性OOP中的封装性是一种信息掩蔽技术,它使系统设计员能够清晰地标明他们所提供的服务界面,用户和应用程序员则只看得见对象提供的操作功能(即封装面上的信息),看不到其中的数据或操作代码细节。从用户或应用程序员的角度讲,对象提供了一组服务,而服务的具体实现(即对象的内部)却对用户屏蔽。如下图所示:headinggo:storeon:用户/应用程序员对象标识符MyPencolorposition
28、downupcolor=blackposition=10.20heading=180系统程序员move:从用户和系统设计者看封装,用户只能看见对象提供的服务,而看不到服务的具体实现,只有系统设计者才能看见封闭在对象中数据及其过程。对象的这一封装机制的目的在于将对象的设计者和使用者分开,使用者不必知道对象行为实现的细节,只须用设计者提供的消息命令对象去做就行了。总之,我们可以这样来定义封装:a一个清楚的边界。对象的所有私有数据、内部程序即方法(成员函数)细节被囿定在这个边界内。b一个接口。这个接口描述了对象之间的相互作用,请求和响应,它就是消息。c对象内部实现代码受到封装壳的保护,其它对象不能直
29、接修改本对象拥有的(私有的)数据和代码,只有通过本对象类的内部代码(成员函数)来修改。这就是OOP中对象的封装性。3多态性OOP支持多态性,它指的是相同的函数调用,被不同的对象接收时,可导致不同的行为。利用多态性,我们可以把函数的不同的实现细节留给接受函数调用的对象,而程序中则用同一函数名进行一般形式的调用。例如,消息函数调用Print()被发送到一图形对象时和将其发送到一正文对象时的结果肯定不一样。例如,有三个函数:abs() 返回整数绝对值labs() 返回long的绝对值fabs() 返回double的绝对值尽管这三个函数执行几乎相同的操作,在SP中,须用三个不同的函数说明及调来表示和使
30、用它们,即大同小异的函数也必须有不同的函数名。于是,虽然仅只是一个求绝对值的运算行为,程序员必须至少记住三件事(三个函数)。在C+中,则改变了这一传统做法,可以用一个名字来表示和访问这三个函数而不会出现混乱。即如下:#includeint abs(int i) return i 0 ? -i: i;double abs(double d) return d 0 ? -d: d;long abs(long l) return l 0 ? -l: l;main() coutabs(-10)“n”; coutabs(-11.0)“n”; coutabs(-9L)“n”;它们是不同的函数,却使用同一名
31、字abs,当程序中调用abs时,OOP编译器会根据自变量类型自动录找正确的那个函数进行运算,这就将要记住的三件事减少为只须记住一件事了,从应用的角度来看,就好象一个函数支持三种不同的运算(功能)一样,这就是函数的多态性。除以上三大特性之外,面向对象程序设计还具有动态连接、消息驱动等特点。所谓连接,是把例程地址送给例程调用者的过程。在运行前的编译时便以目标代码的形式与系统完成连接称为静态连接,而在运行时进行上述连接的则称为动态连接。传统的C仅支持静态连接,而面向对象的程序设计语言在其实现中使用了动态编译技术,因而允许以目标代码的形式在运行过程中才与系统进行连接。这使得系统的灵活性和方便性大大增强
32、。OOP中的消息驱动机制不同于SP中的子程序调用,这是因为,子程序调用者与被调用者有控制与被控制的关系,且凡调用必须有返回。而消息驱动则是当一个对象想激活另一个对象的某个功能时,它把请求以消息的形式传送给接受者就算了事,至于接受对象如何行动,如何处理该消息,完全是接受者自行决定的私事,其行为既不受发送者的控制,也不一定有求必应和必有返回。下图给出一个处理键盘输入的消息驱动过程:Windows应用程序Z系统队列用户按Z键ZWinMain函数消息循环应用程序队列ZZWindows从应用程序的消息循环中接收消息,并处理应用程序窗口的消息窗口函数ZZ作为窗口函数TextOut要求的结果,Windows
33、对应用程序窗口输出“Z”应用程序窗口ZZ注意,上述过程中,每个应用程序将有被创建的一个对应的应用程序队列。3.1.7 面向对象设计方法的特点和面临的问题面向对象设计方法以对象为基础,利用特定的软件工具直接完成从对象客体的描述到软件结构之间的转换。这是面向对象设计方法最主要的特点和成就。面向对象设计方法的应用解决了传统结构化开发方法中客观世界描述工具与软件结构的不一致性问题,缩短了开发周期,解决了从分析和设计到软件模块结构之间多次转换映射的繁杂过程,是一种很有发展前途的系统开发方法。但是同原型方法一样, 面向对象设计方法需要一定的软件基础支持才可以应用,另外在大型的MIS开发中如果不经自顶向下的
34、整体划分,而是一开始就自底向上的采用面向对象设计方法开发系统,同样也会造成系统结构不合理、各部分关系失调等问题。所以面向对象设计方法和结构化方法目前仍是两种在系统开发领域相互依存的、不可替代的方法。3.2 类和对象3.2.1 类和对象概述1类和对象的声明在C程序设计中我们学习过结构体,它的掌握非常重要,重要在哪里呢?重要在结构体和类有相同的特性,但又有很大的区别,类是构成面向对象编程的基础,但它是和结构体有着极其密切的关系。我们在C语言中创建一个结构体我们使用如下方法:struct test private: int number; public: float socre;类的创建方式和结构体
35、几乎一样,看如下的代码:class test private: int number; public: float socre; public: int rp() return number; void setnum(int a) number=a; ;但是大家注意到没有,标准C中是不允许在结构体中声明函数的,但C+中的类可以,这一点就和C有了本质的区别,很好的体现了C+面向对象的特点!过去的C语言是一种非面向对象的语言,它的特性是:程序=算法+数据结构。但C+的特性是:对象=算法+数据结构;程序=对象+对象+对象+对象+.。所以根据这一特性,我们在定义一个自己定义的结构体变量的时候。这个变量
36、就应该是叫做对象或者叫实例。例如:test a;那么a就是test结构的一个对象(实例),test结构体内的成员可以叫做是分量,例如:a.socre=10.1f;那么number就是test结构的对象a的分量(或者叫数据成员,或者叫属性)score;在C语言中结构体中的各成员他们的默认存储控制是public 而 c+中类的默认存储控制是private,所以在类中的成员如果需要外部掉用一定要加上关键字public声明成公有类型,这一特性同样使用于类中的成员函数,函数的操作方式和普通函数差别并不大。例如上面的例子中的rp()成员函数,我们如果有如下定义:test a;调用rp()就应该写成:a.r
37、p();成员函数的调用和普通成员变量的调用方式一致都采用.的操作符。下面给出一个完整的例子:#includeclass testprivate:/私有成员类外不能够直接访问 int number; public:/共有成员类外能够直接访问 float socre; public: int rp() return number; void setnum(int a) number=a; ;void main() test a; /a.number=10;/错误的,私有成员不能外部访问 a.socre=99.9f; couta.socreendl;/公有成员可以外部访问 a.setnum(100)
38、;/通过公有成员函数setnum()对私有成员number进行赋值操作 couta.rp();/间接返回私有成员number的值 cin.get();运行结果:99.91002在类体外定义成员函数 内联函数和外联函数。类的成员函数可以分为内联函数和外联函数。内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内。而说明在类体内,定义在类体外的成员函数叫外联函数。外联函数的函数体定义在类的实现部分。内联函数在调用时不是象一般函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函数中。而是在调用函数处用内联函数体的代码来替换,这样将会节省调用开销,提高运行速度。内联函数与前面讲过
39、的带参数的宏定义比较,它们的代码效率是一样的,但是内联函数要优于宏定义,因为内联函数遵循函数的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关上内联扩展,将与一般函数一样进行调用,调试比较方便。在类体外定义的外联函数可以用一种形式转变成内联函数,即在函数头加上关键字inline就可以了。 重载性。两个以上的函数,取同一个名字,只要使用不同类型的参数或参数个数不同,编译器便知道在什么情况下,调用哪个函数。这就叫做函数重载。成员函数可以进行重载。构造函数可以重载,而析构函数不能重载。一般的成员函数都可以重载。上面介绍了在类内部定义成员函数(方法)的方法,下面我们要介绍一下域区分符(:)
40、的作用。即在类外部定义成员函数。下面我们来看一个例子,利用这个例子中我们要说明几个重要问题:#includeint pp=0;class testprivate:int number;public:float socre;int pp;public:void rp();void test:rp()/在外部利用域区分符定义test类的成员函数:pp=11;/变量名前加域区分符给全局变量pp赋值pp=100;/设置结构体变量void main()test a;test b;a.rp();coutppendl;couta.pppp=100;所以你把上面的成员函数写成如下形势也是正确的:void te
41、st:rp():pp=11;this-pp=100;/this指针就是指向a对象的指针类的成员函数和普通函数一样是可以进行重载操作的,关于重载函数,我们将在后面讨论,这里只给出一个例子看看:#includeclass testprivate:int number;public:float socre;int pp;public:void rp(int);void rp(float);void test:rp(int a)/在外部利用域区分符定义test类的成员函数cout调用成员函数! a:aendl;void test:rp(float a)/在外部利用域区分符定义test类的成员函数cou
42、t调用成员函数! a:aendl;void main()test a;a.rp(100);a.rp(3.14f);cin.get();运行结果:调用成员函数! a:100调用成员函数! a:3.143利用指针和引用调用类的成员函数下面我们来看一下利用指针和利用引用间接调用类的成员函数,对于指针和引用调用成员函数和调用普通函数差别不大,在这里也就不再重复说明了,注意看代码,多试多练习即可。代码如下:#includeclass testprivate:int number;public:float socre;int pp;public:int rp(int);int test:rp(int a)
43、/在外部利用域区分符定义test类的成员函数number=100;return a+number;void run(test *p)/利用指针调用coutrp(100)endl;void run(test &p)/利用引用coutp.rp(200)endl;void main()test a;run(&a);run(a);cin.get();运行结果:2003004类成员的保护性前面我们说过,类的成员如果不显式的生命为public那么它默认的就是private就是私有的,私有声明可以保护成员不能够被外部访问,但在C+还有一个修饰符,它具有和private相似的性能,它就是protected修饰
44、符。在这里我们简单说明一下,他们三着之间的差别:在类的private:节中声明的成员(无论数据成员或是成员函数)仅仅能被类的成员函数和友元访问。在类的protected:节中声明的成员(无论数据成员或是成员函数)仅仅能被类的成员函数,友元以及子类的成员函数和友元访问。在类的public:节中声明的成员(无论数据成员或是成员函数)能被任何人访问。由于private和protected的差别主要是体现在类的继承中,现在的教程还没有设计到友元和子类所以这里不做深入讨论,但上面的三点务必记得,在以后我们会回过头来说明的。总的来说,类成员的保护无非是为了以下四点: 相对与普通函数和其它类的成员函数来说,
45、保护类的数据不能够被肆意的篡改侵犯! 使类对它本身的内部数据维护负责,只有类自己才能够访问自己的保护数据! 限制类的外部接口,把一个类分成公有的和受保护的两部分,对于使用者来说它只要会用就可以,无须了解内部完整结构,起到黑盒的效果。 减少类与其它代码的关联程,类的功能是独立的,不需要依靠应用程序的运行环境,这个程序可以用它,另外一个也可以用它,使得你可以轻易的用一个类替换另一个类。下面为了演示类成员的保护特性,我们来做一个球类游戏!我们设计一个类,来计算球员的平均成绩,要求在外部不能够随意篡改球员的平均成绩。我们把该类命名为ballscore并且把它放到ballscore.h的有文件中!cla
46、ss ballscoreprotected:int gbs;/好球单位得分int bbs;/坏球单位扣分float gradescore;/平均成绩public:void init()gbs=5;bbs=-3;float GetGS(float goodball,float badball)/goodball好球数,badball坏球数gradescore=(goodball*gbs+badball*bbs)/(goodball+badball);return gradescore;/返回平均成绩;主函数调用:#include #include ballscore.hvoid main()ba
47、llscore jeff;coutjeff.GetGS(10,3);/jeff.gradescore=5.5;/想篡改jeff的平均成绩是错误的!jeff.init();cin.get();在上面的代码中头文件和类的使用很好了体现了类的黑盒特性,谁也不能够在外部修改球员的平均成绩。5类的作用域下面我们说一下关于类的作用域。在说内容之前我们先给出这部分内容的一个完整代码,看讲解的是参照此一下代码。#includeclass ballscoreprotected:int gbs;/好球单位得分int bbs;/坏球单位扣分float gscore;/平均成绩public:void init()gb
48、s=5;bbs=-3;float GetGS(float gb,float bb)int gscore=0;/定义一个同名的类成员函数局部变量ballscore:gscore=(gb*gbs+bb*bbs)/(gb+bb);/由于同名须加类名和域区分符return ballscore:gscore;/返回平均成绩;int ballscore=0;/定义一个与类名称相同的普通全局变量int test;void main()class test/局部类的创建float a;float b;test test;:test=1;/由于局部类名隐藏了外部变量,使用时需加域区分符class ballsco
49、re jeff;/由于全局ballsocre和类名称相同,需加class前缀jeff.init();coutjeff.GetGS(10,3);类的作用域是只指定义和相应的成员函数定义的范围,在该范围内,一个类的成员函数对同一类的数据成员具有无限制的访问权。3.2.1 构造函数与析构函数构造函数和析构函数是类的两个特殊的成员函数1构造函数构造函数(constructor)是类的一个特殊的成员函数,它与类名同名。当定义该类的对象时,构造函数将被系统自动调用用以实现对该对象的初始化。构造函数不能有返回值,因而不能指定包括void在内的任何返回值类型。构造函数的定义与其他成员函数的定义一样可以放在类内
50、或类外。构造函数的定义格式为:类名(形参说明)函数体构造函数既可以定义成有参函数,也可以定义成无参函数,要根据问题的需要来定。注意:程序中不能直接调用构造函数,构造函数是在创建对象时由系统直接调用的,因此,在构造函数中一般完成初始化类成员变量的操作。2构造函数的重载一个类中出现了两个以上的同名的成员函数时,称为类的成员函数的重载。在类的成员函数的重载中,比较常见形式是构造函数的重载,当类中出现了重载构造函数时,C+语言将根据构造函数中的参数个数和类型选择合适的构造函数来完成对象的构造。3默认构造函数与缺省参数的构造函数如果在类中没有显示定义构造函数,则编译系统会为该类提供一个默认的构造函数,该默认构造函数是一个无参函数,函数体为空,它仅仅负责创建对象,而不做任何初始化工作(即不给相应的数据成员赋初值),所以在该类的对象创建时不能保证有一个确定的初始状态。良好的编程习惯应该是给类提供合适的完成初始化工作的构造函数。但是,只要一个类定义了一个构造函数(不一定是无参构造函数),编译系统就不再提供默认的构造函数。当构造函数具有缺省参数时,称为具有缺省参数的构造函数,在使用具有缺省参数的构造函数时,要防止二义性。4拷贝构造函数拷贝构造函数是一种特殊的构造函数。定义拷贝构造函数的一般格式为:类名:类名(const类名 &形式参数)函数体拷贝构造函数的函数名与类名同名。该函数也没有返回值
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 车辆安全检查记录表【8篇】
- 安全生产宣讲音频讲解
- 破碎机安全试题及答案
- 铸造技能考试题库及答案
- 药品报损报废制度试题及答案
- 医患沟通规范试题及答案
- 上海非编考试试题及答案
- 社区治理服务规范考核试题及答案
- 市政道路冬季施工方案
- 帅康橱柜无锡地区营销策划方案
- 外墙瓷砖维修方案
- 高等职业学校汽车智能技术专业实训教学条件建设标准
- 钢构厂房施工合同范本(2024版)
- 夜间施工安全培训
- 《论语》全文原文版
- TB 10752-2018 高速铁路桥涵工程施工质量验收标准
- 盐城工业职业技术学院单招职业技能测试参考试题库(含答案)
- 《人体中的化学反应》课件
- (沪教牛津版)深圳市小学1-6年级英语单词默写表(英文+中文+默写)
- 游泳救生员培训课件
- 2023学年完整公开课版《字母表》教学
评论
0/150
提交评论