1、单击此处编辑母版标题样式,单击此处编辑母版副标题样式,第8章 类和对象,第9章 关于类和对象的进一步讨论,第10章 运算符重载,第3篇基于对象的程序设计,第8章 类和对象,8.1,面向对象程序设计方法概述,8.2 类的声明和对象的定义,8.3 类的成员函数,8.4 对象成员的引用,8.5 类的封装性和信息隐蔽,8.6 类和对象的简单应用举例,到目前为止,我们介绍的是,C+,在面向过程的程序设计中的应用。对于规模比较小的程序,编程者可以直接编写出一个面向过程的程序,详细地描述每一瞬时的数据结构及对其的操作过程。但是当程序规模较大时,就显得力不从心了。,C+,就是为了解决编写大程序过程中的困难而产
2、生的。,8.1 面向对象程序设计方法概述,面向对象的程序设计的思路和人们日常生活中处理问题的思路是相似的。在自然世界和社会生活中,一个复杂的事物总是由许多部分组成的。,当人们生产汽车时,分别设计和制造发动机、底盘、车身和轮子,最后把它们组装在一起。在组装时,各部分之间有一定的联系,以便协调工作。,这就是面向对象的程序设计的基本思路。,为了进一步说明问题,下面先讨论几个有关的概念。,8.1.1 什么是面向对象的程序设计,1.对象,客观世界中任何一个事物都可以看成一个对象(,object)。,对象可大可小。对象是构成系统的基本单位。,任何一个对象都应当具有这两个要素,即属性(,attribute)
3、,和行为(,behavior),,它能根据外界给的信息进行相应的操作。一个对象往往是由一组属性和一组行为构成的。一般来说,凡是具备属性和行为这两种要素的,都可以作为对象。,在一个系统中的多个对象之间通过一定的渠道相互联系,如图8.1示意。要使某一个对象实现某一种行为(即操作),应当向它传送相应的消息。对象之间就是这样通过发送和接收消息互相联系的。,图8.1 图8.2,面向对象的程序设计采用了以上人们所熟悉的这种思路。使用面向对象的程序设计方法设计一个复杂的软件系统时,首要的问题是确定该系统是由哪些对象组成的,并且设计这些对象。在,C+,中,每个对象都是由数据和函数(即操作代码)这两部分组成的,
4、见图8.2。数据体现了前面提到的,“,属性,”,,如一个三角形对象,它的3个边长就是它的属性。函数是用来对数据进行操作的,以便实现某些功能,例如可以通过边长计算出三角形的面积,并且输出三角形的边长和面积。计算三角形面积和输出有关数据就是前面提到的行为,在程序设计方法中也称为方法(,method)。,调用对象中的函数就是向该对象传送一个消息(,message),,要求该对象实现某一行为(功能)。,2.封装与信息隐蔽,可以对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,也就是说从外界是看不到的,甚至是不可知的。,这样做的好处是大大降低了操作对象的复杂程度。,面向对象程序设计方法的一个重要
5、特点就是,“,封装性,”,(,encapsulation),,所谓,“,封装,”,,指两方面的含义:一是将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相对独立,互不干扰。二是将对象中某些部分对外隐蔽,即隐蔽其内部细节,只留下少量接口,以便与外界联系,接收外界的消息。这种对外界隐蔽的做法称为信息隐蔽(,imformation hiding)。,信息隐蔽还有利于数据安全,防止无关的人了解和修改数据。,C+,的对象中的函数名就是对象的对外接口,外界可以通过函数名来调用这些函数来实现某些行为(功能)。这些将在以后详细介绍。,3.抽象,在程序设计方法中,常用到抽象(,abstra
6、ction),这一名词。抽象的过程是将有关事物的共性归纳、集中的过程。,抽象的作用是表示同一类事物的本质。,C,和,C+,中的数据类型就是对一批具体的数的抽象。,对象是具体存在的,如一个三角形可以作为一个对象,10个不同尺寸的三角形是10个对象。如果这10个三角形对象有相同的属性和行为,可以将它们抽象为一种类型,称为三角形类型。在,C+,中,这种类型就称为,“,类(,class),”,。,这10个三角形就是属于同一,“,类,”,的对象。类是对象的抽象,而对象则是类的特例,或者说是类的具体表现形式。,4.继承与重用,如果在软件开发中已经建立了一个名为,A,的,“,类,”,,又想另外建立一个名为,
7、B,的,“,类,”,,而后者与前者内容基本相同,只是在前者的基础上增加一些属性和行为,只需在类,A,的基础上增加一些新内容即可。这就是面向对象程序设计中的继承机制。利用继承可以简化程序设计的步骤。,“,白马,”,继承了,“,马,”,的基本特征,又增加了新的特征(颜色),,“,马,”,是父类,或称为基类,,“,白马,”,是从,“,马,”,派生出来的,称为子类或派生类。,C+,提供了继承机制,采用继承的方法可以很方便地利用一个已有的类建立一个新的类。这就是常说的,“,软件重用,”,(,software reusability),的思想。,5.多态性,如果有几个相似而不完全相同的对象,有时人们要求在
8、向它们发出同一个消息时,它们的反应各不相同,分别执行不同的操作。这种情况就是多态现象。如,在,Windows,环境下,用鼠标双击一个文件对象(这就是向对象传送一个消息),如果对象是一个可执行文件,则会执行此程序,如果对象是一个文本文件,则启动文本编辑器并打开该文件。,在,C+,中,所谓多态性(,polymorphism),是指:由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应。多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。,传统的面向过程程序设计是围绕功能进行的,用一个函数实现一个功能。所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数所使
9、用(见图8.3)。,图8.3,8.1.2 面向对象程序设计的特点,面向对象程序设计采取的是另外一种思路。它面对的是一个个对象。实际上,每一组数据都是有特定的用途的,是某种操作的对象。也就是说,一组操作调用一组数据。,程序设计者的任务包括两个方面:一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起;二是考虑怎样向有关对象发送消息,以完成所需的任务。这时他如同一个总调度,不断地向各个对象发出命令,让这些对象活动起来(或者说激活这些对象),完成自己职责范围内的工作。各个对象的操作完成了,整体任务也就完成了。显然,对一个大型任务来说,面向对象程序设计方法是十分有效的,它能大大降低程序设计人员
10、的工作难度,减少出错机会。,类是,C+,中十分重要的概念,它是实现面向对象程序设计的基础。类是所有面向对象的语言的共同特征,所有面向对象的语言都提供了这种类型。一个有一定规模的,C+,程序是由许多类所构成的。,C+,支持面向过程的程序设计,也支持基于对象的程序设计,又支持面向对象的程序设计。在本章到第10章将介绍基于对象的程序设计。包括类和对象的概念、类的机制和声明、类对象的定义与使用等。这是面向对象的程序设计的基础。,8.1.3 类和对象的作用,基于对象就是基于类。与面向过程的程序不同,基于对象的程序是以类和对象为基础的,程序的操作是围绕对象进行的。在此基础上利用了继承机制和多态性,就成为面
11、向对象的程序设计(有时不细分基于对象程序设计和面向对象程序设计,而把二者合称为面向对象的程序设计)。,基于对象程序设计所面对的是一个个对象。所有的数据分别属于不同的对象。,在面向过程的结构化程序设计中,人们常使用这样的公式来表述程序:,程序=算法数据结构,算法和数据结构两者是互相独立、分开设计的,面向过程的程序设计是以算法为主体的。在实践中人们逐渐认识到算法和数据结构是互相紧密联系不可分的,应当以一个算法对应一组数据结构,而不宜提倡一个算法对应多组数据结构,以及一组数据结构对应多个算法。基于对象和面向对象程序设计就是把一个算法和一组数据结构封装在一个对象中。因此,就形成了新的观念:,对象=算法
12、 数据结构,程序=(对象+对象+对象+,)+消息 或:程序=对象,s+,消息,“,对象,s,”,表示多个对象。消息的作用就是对对象的控制。程序设计的关键是设计好每一个对象,及确定向这些对象发出的命令,使各对象完成相应操作。,随着软件规模的迅速增大,软件人员面临的问题十分复杂。需要规范整个软件开发过程,明确软件开发过程中每个阶段的任务,在保证前一个阶段工作的正确性的情况下,再进行下一阶段的工作。这就是软件工程学需要研究和解决的问题。,面向对象的软件工程包括以下几个部分:,8.1.4 面向对象的软件开发,1.面向对象分析(,object oriented analysis,OOA),软件工程中的系
13、统分析阶段,系统分析员要和用户结合在一起,对用户的需求作出精确的分析和明确的描述,从宏观的角度概括出系统应该做什么(而不是怎么做)。面向对象的分析,要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,归纳出有关的对象(包括对象的属性和行为)以及对象之间的联系,并将具有相同属性和行为的对象用一个类(,class),来表示。建立一个能反映真实工作情况的需求模型。,2.面向对象设计(,object oriented design,OOD),根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计,首先是进行类的设计,类的设计可能包含多个层次(利用继承与派生)。然后
14、以这些类为基础提出程序设计的思路和方法,包括对算法的设计。在设计阶段,并不牵涉某一种具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述。,3.面向对象编程(,object oriented programming,OOP),根据面向对象设计的结果,用一种计算机语言把它写成程序,显然应当选用面向对象的计算机语言(例如,C+),,否则无法实现面向对象设计的要求。,4.面向对象测试(,object oriented test,OOT),在写好程序后交给用户使用前,必须对程序进行严格的测试。测试的目的是发现程序中的错误并改正它。面向对象测试是用面向对象的方法进行测试,以类作为测试的
15、基本单元。,5.面向对象维护(,object oriented soft maintenance,OOSM),因为对象的封装性,修改一个对象对其他对象影响很小。利用面向对象的方法维护程序,大大提高了软件维护的效率。,现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成的。这样,,OOP,阶段的任务就比较简单了,程序编写者只需要根据,OOD,提出的思路用面向对象语言编写出程序即可。在一个大型软件的开发中,,OOP,只是面向对象开发过程中的一个很小的部分。,如果所处理的是一个较简单的问题,可以不必严格按照以上5个阶段进
16、行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计(或选用已有的类)和程序的设计。,每一个实体都是对象。有一些对象是具有相同的结构和特性的。每个对象都属于一个特定的类型。,在,C+,中对象的类型称为类(,class)。,类代表了某一批对象的共性和特征。前面已说明:类是对象的抽象,而对象是类的具体实例(,instance)。,正如同结构体类型和结构体变量的关系一样,人们先声明一个结构体类型,然后用它去定义结构体变量。同一个结构体类型可以定义出多个不同的结构体变量。,8.2 类的声明和对象的定义,8.2.1 类和对象的关系,在,C+,中也是先声明一个类类型,然后用它去定义若干个同类型
17、的对象。对象就是类类型的一个变量。可以说类是对象的模板,是用来定义对象的一种抽象类型。,类是抽象的,不占用内存,而对象是具体的,占用存储空间。在一开始时弄清对象和类的关系是十分重要的。,类是用户自己指定的类型。如果程序中要用到类类型,必须自己根据需要进行声明,或者使用别人已设计好的类。,C+,标准本身并不提供现成的类的名称、结构和内容。,在,C+,中声明一个类类型和声明一个结构体类型是相似的。,下面是声明一个结构体类型的方法:,struct Student /,声明了一个名为,Student,的结构体类型,int num;,char name20;,char sex;,;,Student st
18、ud1,stud2;/,定义了两个结构体变量,stud1,和,stud2,8.2.2 声明类类型,它只包括数据,没有包括操作。现在声明一个类:,class Student /,以,class,开头,int num;,char name20;,char sex;/,以上3行是数据成员,void display()/,这是成员函数,coutnum:numendl;,coutname:nameendl;,coutsex:sexendl;/,以上4行是函数中的操作语句,;,Student stud1,stud2;/,定义了两个,Student,类的对象,stud1,和,stud2,可以看到声明类的方法
19、是由声明结构体类型的方法发展而来的。,可以看到,类(,class),就是对象的类型。实际上,类是一种广义的数据类型。类这种数据类型中的数据既包含数据,也包含操作数据的函数。,不能把类中的全部成员与外界隔离,一般是把数据隐蔽起来,而把成员函数作为对外界的接口。,可以将上面类的声明改为,class Student /,声明类类型,private:/,声明以下部分为私有的,int num;,char name20;,char sex;,public:/,声明以下部分为公用的,void display(),coutnum:numendl;,coutname:nameendl;,coutsex:sexe
20、ndl;,;,Student stud1,stud2;/,定义了两个,Student,类的对象,如果在类的定义中既不指定,private,,也不指定,public,,则系统就默认为是私有的。,归纳以上对类类型的声明,可得到其一般形式如下:,class,类名,private:,私有的数据和成员函数;,public:,公用的数据和成员函数;,;,private,和,public,称为成员访问限定符(,member access specifier)。,除了,private,和,public,之外,还有一种成员访问限定符,protected(,受保护的),用,protected,声明的成员称为受保护
21、的成员,它不能被类外访问(这点与私有成员类似),但可以被派生类的成员函数访问。,在声明类类型时,声明为,private,的成员和声明为,public,的成员的次序任意,既可以先出现,private,部分,也可以先出现,public,部分。如果在类体中既不写关键字,private,,又不写,public,,就默认为,private。,在一个类体中,关键字,private,和,public,可以分别出现多次。每个部分的有效范围到出现另一个访问限定符或类体结束时(最后一个右花括号)为止。但是为了使程序清晰,应该养成这样的习惯:使每一种成员访问限定符在类定义体中只出现一次。,在以前的,C+,程序中,常
22、先出现,private,部分,后出现,public,部分,如上面所示。现在的,C+,程序多数先写,public,部分,把,private,部分放在类体的后部。这样可以使用户将注意力集中在能被外界调用的成员上,使阅读者的思路更清晰一些。,在,C+,程序中,经常可以看到类。为了用户方便,常用的,C+,编译系统往往向用户提供类库(但不属于,C+,语言的组成部分),内装常用的基本的类,供用户使用。不少用户也把自己或本单位经常用到的类放在一个专门的类库中,需要用时直接调用,这样就减少了程序设计的工作量。,8.2.2节的程序段中,最后一行用已声明的,Student,类来定义对象,这种方法是很容易理解的。经
23、过定义后,,stud1,和,stud2,就成为具有,Student,类特征的对象。,stud1,和,stud2,这两个对象都分别包括,Student,类中定义的数据和函数。,定义对象也可以有几种方法。,8.2.3 定义对象的方法,1.先声明类类型,然后再定义对象,前面用的就是这种方法,如,Student stud1,stud2;/Student,是已经声明的类类型,在,C+,中,声明了类类型后,定义对象有两种形式。,(1),class,类名 对象名,如,class Student stud1,stud2;,把,class,和,Student,合起来作为一个类名,用来定义对象。,(2)类名 对象
24、名,如,Student stud1,stud2;,直接用类名定义对象。这两种方法是等效的。第1种方法是从,C,语言继承下来的,第2种方法是,C+,的特色,显然第2种方法更为简捷方便。,2.在声明类类型的同时定义对象,class Student /,声明类类型,public:/,先声明公用部分,void display(),coutnum:numendl;,coutname:nameendl;,coutsex:sexendl;,private:/,后声明私有部分,int num;,char name20;,char sex;,stud1,stud2;/,定义了两个,Student,类的对象,在定
25、义,Student,类的同时,定义了两个,Student,类的对象。,3.不出现类名,直接定义对象,class /,无类名,private:/,声明以下部分为私有的,public:/,声明以下部分为公用的,stud1,stud2;/,定义了两个无类名的类对象,直接定义对象,在,C+,中是合法的、允许的,但却很少用,也不提倡用。在实际的程序开发中,一般都采用上面3种方法中的第1种方法。在小型程序中或所声明的类只用于本程序时,也可以用第2种方法。,在定义一个对象时,编译系统会为这个对象分配存储空间,以存放对象中的成员。,C+,增加了,class,类型后,仍保留了结构体类型(,struct),,而且
26、把它的功能也扩展了。,C+,允许用,struct,来定义一个类型。如可以将前面用关键字,class,声明的类类型改为用关键字,struct:,struct Student /,用关键字,struct,来声明一个类类型,private:/,声明以下部分为私有的,int num;/,以下3行为数据成员,char name20;,char sex;,public:/,声明以下部分为公用的,void display()/,成员函数,coutnum:numendl;,coutname:nameendl;,coutsex:sexendl;,;,Student stud1,stud2;/,定义了两个,Stu
27、dent,类的对象,8.2.4 类和结构体类型的异同,为了使结构体类型也具有封装的特征,,C+,不是简单地继承,C,的结构体,而是使它也具有类的特点,以便于用于面向对象程序设计。用,struct,声明的结构体类型实际上也就是类。,用,struct,声明的类,如果对其成员不作,private,或,public,的声明,系统将其默认为,public,。如果想分别指定私有成员和公用成员,则应用,private,或,public,作显式声明。而用,class,定义的类,如果不作,private,或,public,声明,系统将其成员默认为,private,,在需要时也可以自己用显式声明改变。,如果希望成
28、员是公用的,使用,struct,比较方便,如果希望部分成员是私有的,宜用,class。,建议尽量使用,class,来建立类,写出完全体现,C+,风格的程序。,类的成员函数(简称类函数)是函数的一种,它的用法和作用和第4章介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中。它可以被指定为,private(,私有的)、,public(,公用的)或,protected(,受保护的)。在使用类函数时,要注意调用它的权限(它能否被调用)以及它的作用域(函数能使用什么范围中的数据和函数)。例如私有的成员函数只能被本类中的其他成员函数所调用,而不能
29、被类外调用。,8.3 类的成员函数,8.3.1 成员函数的性质,成员函数可以访问本类中任何成员(包括私有的和公用的),可以引用在本作用域中有效的数据。,一般的做法是将需要被外界调用的成员函数指定为,public,,它们是类的对外接口。但应注意,并非要求把所有成员函数都指定为,public。,有的函数并不是准备为外界调用的,而是为本类中的成员函数所调用的,就应该将它们指定为,private。,这种函数的作用是支持其他函数的操作,是类中其他成员的工具函数(,utility function),,类外用户不能调用这些私有的工具函数。,类的成员函数是类体中十分重要的部分。如果一个类中不包含成员函数,就
30、等同于,C,语言中的结构体了,体现不出类在面向对象程序设计中的作用。,在前面已经看到成员函数是在类体中定义的。也可以在类体中只写成员函数的声明,而在类的外面进行函数定义。如,class Student,public:,void display();/,公用成员函数原型声明,private:,int num;,string name;,char sex;/,以上3行是私有数据成员,;,void Studentdisplay()/,在类外定义,display,类函数,coutnum:numendl;/,函数体,coutname:nameendl;,coutsex:sexendl;,Student
31、stud1,stud2;/,定义两个类对象,8.3.2 在类外定义成员函数,注意:在类体中直接定义函数时,不需要在函数名前面加上类名,因为函数属于哪一个类是不言而喻的。但成员函数在类外定义时,必须在函数名前面加上类名,予以限定(,qualifed),,“,”,是作用域限定符(,field qualifier),或称作用域运算符,用它声明函数是属于哪个类的。,如果在作用域运算符,“,”,的前面没有类名,或者函数名前面既无类名又无作用域运算符,“,”,,如,display(),或,display(),则表示,display,函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普
32、通函数。,类函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前,否则编译时会出错。,虽然函数在类的外部定义,但在调用成员函数时会根据在类中声明的函数原型找到函数的定义(函数代码),从而执行该函数。,在类的内部对成员函数作声明,而在类体外定义成员函数,这是程序设计的一种良好习惯。如果一个函数,其函数体只有23行,一般可在声明类时在类体中定义。多于3行的函数,一般在类体内声明,在类外定义。,关于内置(,inline),函数,已在第4章第4.5节中作过介绍。类的成员函数也可以指定为内置函数。,在类体中定义的成员函数的规模一般都很小,而系统调用函数的过程所花费的时间开销相
33、对是比较大的。调用一个函数的时间开销远远大于小规模函数体中全部语句的执行时间。为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,,C+,系统会自动将它们作为内置(,inline),函数来处理。也就是说,在程序调用这些成员函数时,并不是真正地执行函数的调用过程(如保留返回地址等处理),而是把函数代码嵌入程序的调用点。这样可以大大减少调用成员函数的时间开销。,8.3.3,inline,成员函数,C+,要求对一般的内置函数要用关键字,inline,声明,但对类内定义的成员函数,可以省略,inline,,因为这些成员函数已被隐含地指定为内置函数。如,class Student,pub
34、lic:,void display(),coutnum:numendl;,coutname:nameendl;,coutsex:sexendl;,private:,int num;,string name;,char sex;,;,其中第3行,void display(),也可以写成,inline void display(),将,display,函数显式地声明为内置函数。以上两种写法是等效的。对在类体内定义的函数,一般都省写,inline。,应该注意的是:如果成员函数不在类体内定义,而在类体外定义,系统并不把它默认为内置(,inline),函数,调用这些成员函数的过程和调用一般函数的过程是相
35、同的。如果想将这些成员函数指定为内置函数,应当用,inline,作显式声明。如,class Student,public:,inline void display();/,声明此成员函数为内置函数,private:,int num;,string name;,char sex;,;,inline void Studentdisplay()/,在类外定义,display,函数为内置函数,coutnum:numendl;,coutname:nameendl;,coutsex:sexabc;,;,可以用下面的语句来输出该类对象所占用的字节数:,coutsizeof(Time)hour,(*p).ho
36、ur,和,t.hour,三者等价。,8.4.2 通过指向对象的指针访问对象中的成员,如果为一个对象定义了一个引用变量,它们是共占同一段存储单元的,实际上它们是同一个对象,只是用不同的名字表示而已。因此完全可以通过引用变量来访问对象中的成员。,如果已声明了,Time,类,并有以下定义语句:,Time t1;/,定义对象,t1,Time /,定义,Time,类引用变量,t2,,并使之初始化为,t1,coutt2.hour;/,输出对象,t1,中的成员,hour,由于,t2,与,t1,共占同一段存储单元(即,t2,是,t1,的别名),因此,t2.hour,就是,t1.hour。,本章第8.6节的例8
37、.2中的程序(,b),介绍的是引用变量作为形参的情况,读者可以参考。,8.4.3 通过对象的引用变量来访问对象中的成员,从前面的介绍已知:,C+,通过类来实现封装性,把数据和与这些数据有关的操作封装在一个类中,或者说,类的作用是把数据和算法封装在用户声明的抽象数据类型中。,在声明了一个类以后,用户主要是通过调用公用的成员函数来实现类提供的功能(例如对数据成员设置值,显示数据成员的值,对数据进行加工等)。因此,公用成员函数是用户使用类的公用接口(,public interface),,或者说是类的对外接口。,8.5 类的封装性和信息隐蔽,8.5.1 公用接口与私有实现的分离,当然并不一定要把所有
38、成员函数都指定为,public(,公用)的,但这时这些成员函数就不是公用接口了。在类外虽然不能直接访问私有数据成员,但可以通过调用公用成员函数来引用甚至修改私有数据成员。,用户可以调用公用成员函数来实现某些功能,而这些功能是在声明类时已指定的,用户可以使用它们而不应改变它们。实际上用户往往并不关心这些功能是如何实现的细节,而只需知道调用哪个函数会得到什么结果,能实现什么功能即可。,通过成员函数对数据成员进行操作称为类的实现,为了防止用户任意修改公用成员函数,改变对数据进行的操作,往往不让用户看到公用成员函数的源代码,显然更不能修改它,用户只能接触到公用成员函数的目标代码(详见8.5.2节)。,
39、可以看到:类中被操作的数据是私有的,实现的细节对用户是隐蔽的,这种实现称为私有实现(,private implementation)。,这种,“,类的公用接口与私有实现的分离,”,形成了信息隐蔽。,软件工程的一个最基本的原则就是将接口与实现分离,信息隐蔽是软件工程中一个非常重要的概念。它的好处在于:,(1)如果想修改或扩充类的功能,只需修改本类中有关的数据成员和与它有关的成员函数,程序中类外的部分可以不必修改。,(2)如果在编译时发现类中的数据读写有错,不必检查整个程序,只需检查本类中访问这些数据的少数成员函数。,在面向对象的程序开发中,一般做法是将类的声明(其中包含成员函数的声明)放在指定的
40、头文件中,用户如果想用该类,只要把有关的头文件包含进来即可,不必在程序中重复书写类的声明,以减少工作量,节省篇幅,提高编程的效率。,由于在头文件中包含了类的声明,因此在程序中就可以用该类来定义对象。由于在类体中包含了对成员函数的声明,在程序中就可以调用这些对象的公用成员函数。为了实现上一节所叙述的信息隐蔽,对类成员函数的定义一般不放在头文件中,而另外放在一个文件中。,8.5.2 类声明和成员函数定义的分离,例如,可以分别写两个文件:,/,student.h (,这是头文件,在此文件中进行类的声明),class Student /,类声明,public:,void display();/,公用成
41、员函数原型声明,private:,int num;,char name20;,char sex;,;,/student.cpp /,在此文件中进行函数的定义,#,include,#include student.h /,不要漏写此行,否则编译通不过,void Studentdisplay()/,在类外定义,display,类函数,coutnum:numendl;,coutname:nameendl;,coutsex:sexendl;,为了组成一个完整的源程序,还应当有包括主函数的源文件:,/,main.cpp,主函数模块,#,include,#include student.h /,将类声明头
42、文件包含进来,int main(),Student stud;/,定义对象,stud.display();/,执行,stud,对象的,display,函数,return 0;,这是一个包括3个文件的程序,组成两个文件模块:一个是主模块,main.cpp,,一个是,student.cpp。,在主模块中又包含头文件,student.h。,在预编译时会将头文件,student.h,中的内容取代#,include student.h,行。,请注意:由于将头文件,student.h,放在用户当前目录中,因此在文件名两侧用双撇号包起来(,student.h),而不用尖括号(),,否则编译时会找不到此文件。
43、,图8.6。,在运行程序时调用,stud,中的,display,函数,输出各数据成员的值。,如果一个类声明多次被不同的程序所选用,每次都要对包含成员函数定义的源文件(如上面的,student.cpp),进行编译,这是否可以改进呢?的确,可以不必每次都对它重复进行编译,而只需编译一次即可。把第一次编译后所形成的目标文件保存起来,以后在需要时把它调出来直接与程序的目标文件相连接即可。这和使用函数库中的函数是类似的。,这也是把成员函数的定义不放在头文件中的一个好处。,在实际工作中,并不是将一个类声明做成一个头文件,而是将若干个常用的功能相近的类声明集中在一起,形成类库。,类库有两种:一种是,C+,编
44、译系统提供的标准类库;一种是用户根据自己的需要做成的用户类库,提供给自己和自己授权的人使用,这称为自定义类库。在程序开发工作中,类库是很有用的,它可以减少用户自己对类和成员函数进行定义的工作量。,类库包括两个组成部分:(1)类声明头文件;(2)已经过编译的成员函数的定义,它是目标文件。用户只需把类库装入到自己的计算机系统中(一般装到,C+,编译系统所在的子目录下),并在程序中用#,include,命令行将有关的类声明的头文件包含到程序中,就可以使用这些类和其中的成员函数,顺利地运行程序。,这和在程序中使用,C+,系统提供的标准函数的方法是一样的,例如用户在调用,sin,函数时只需将包含声明此函
45、数的头文件包含到程序中,即可调用该库函数,而不必了解,sin,函数是怎么实现的(函数值是怎样计算出来的)。当然,前提是系统已装了标准函数库。在用户源文件经过编译后,与系统库(是目标文件)相连接。,在用户程序中包含类声明头文件,类声明头文件就成为用户使用类的公用接口,在头文件的类体中还提供了成员函数的函数原型声明,用户只有通过头文件才能使用有关的类。用户看得见和接触到的是这个头文件,任何要使用这个类的用户只需包含这个头文件即可。包含成员函数定义的文件就是类的实现。请特别注意:类声明和函数定义一般是分别放在两个文本中的。,由于要求接口与实现分离,为软件开发商向用户提供类库创造了很好的条件。开发商把
46、用户所需的各种类的声明按类放在不同的头文件中,同时对包含成员函数定义的源文件进行编译,得到成员函数定义的目标代码。软件商向用户提供这些头文件和类的实现的目标代码(不提供函数定义的源代码)。用户在使用类库中的类时,只需将有关头文件包含到自己的程序中,并且在编译后连接成员函数定义的目标代码即可。,由于类库的出现,用户可以像使用零件一样方便地使用在实践中积累的通用的或专用的类,这就大大减少了程序设计的工作量,有效地提高了工作效率。,类的成员函数在面向对象程序理论中被称为,“,方法,”,(,method),,“,方法,”,是指对数据的操作。一个,“,方法,”,对应一种操作。显然,只有被声明为公用的方法
47、(成员函数)才能被对象外界所激活。外界是通过发,“,消息,”,来激活有关方法的。所谓,“,消息,”,,其实就是一个命令,由程序语句来实现。前面的,stud.display();,就是向对象,stud,发出的一个,“,消息,”,,通知它执行其中的,display,“,方法,”,(即,display,函数)。上面这个语句涉及3个术语:对象、方法和消息。,stud,是对象,,display(),是方法,语句,“,stud.display();,”,是消息。,8.5.3 面向对象程序设计中的几个名词,例8.1 最简单的例子。,#,include,using namespace std;,class T
48、ime /,定义,Time,类,public:/,数据成员为公用的,int hour;,int minute;,int sec;,;,int main(),Time t1;/,定义,t1,为,Time,类对象,cint1.hour;/,输入设定的时间,cint1.minute;,cint1.sec;,coutt1.hour:t1.minute:t1.sect1.hour;/,向,t1,的数据成员输入数据,cint1.minute;,cint1.sec;,coutt1.hour:t1.minute:t1.sect2.hour;/,向,t2,的数据成员输入数据,cint2.minute;,cint
49、2.sec;,coutt2.hour:t2.minute:t2.sect.hour;/,输入设定的时间,cint.minute;,cint.sec;,void show_time(Time&t)/,定义函数,show_time,,形参,t,是引用变量,coutt.hour:t.minute:t.secendl;/,输出对象中的数据,运行情况与程序(,a),相同。,(3)程序(,c),可以对上面的程序作一些修改,数据成员的值不再由键盘输入,而在调用函数时由实参给出,并在函数中使用默认参数。将程序(,b),第8行以下部分改为,int main(),void set_time(Time /,函数声明
50、,void show_time(Time /,函数声明,Time t1;,set_time(t1,12,23,34);/,通过实参传递时、分、秒的值,show_time(t1);,Time t2;,set_time(t2);/,使用默认的时、分、秒的值,show_time(t2);,return 0;,void set_time(Time&t,int hour,int minute,int sec),t.hour=hour;,t.minute=minute;,t.sec=sec;,void show_time(Time&t),coutt.hour:t.minute:t.sechour;,cin