1、C+面向对象程序设计(第二版)第四章 类与对象2023年12月18日星期一C+面向对象程序设计第四章 类与对象t4.1 面向对象程序设计的基本特点t4.2 建立类与对象t4.3 构造函数和析构函数t4.4 类的作用域和对象的生存期t4.5 类的静态成员t4.6 常类型t4.7 数组、指针与对象 t4.8 类的组合2023年12月18日星期一C+面向对象程序设计第四章 类与对象t4.9 友元t项目设计3 面向对象程序设计中类的应用 2023年12月18日星期一C+面向对象程序设计4.1 面向对象程序设计的基本特点t4.1.1 抽象t4.1.2 封装t4.1.3 继承t4.1.4 多态2023年1
2、2月18日星期一C+面向对象程序设计4.1.1 抽象t抽象是面向对象程序设计的基本原则之一,抽象与具体相对应。l一个汽车的型号就是抽象,它代表了某个汽车的一切属性,包括最大时速,车身长度,全车净重等。抽象就是对具体事物的概括。t在现实中,为了减少必须处理的事情,我们是在某一程度的细节中生活的。在面向对象程序设计中,这种细节程度就叫抽象。2023年12月18日星期一C+面向对象程序设计4.1.2 封装t在面向对象程序设计中抽象可以表示它所描述事物的所有属性和操作,将抽象得到的数据和操作相结合,形成一个有机的整体,就叫做封装。l在C+中,是利用类(class)的形式来实现封装的,可以通过封装,将一
3、部分操作或属性作为类与外部的接口,将其他成员隐蔽起来,以达到对数据访问权限的合理控制,使程序中不同部分之间的相互影响减到最低限度。2023年12月18日星期一C+面向对象程序设计4.1.2 封装t封装可以将数据和代码组织为一个可重用的C+类,在编写代码的时候就可以有效利用已有的成果。l在C+中如果以电视为例进行抽象并封装的话,可以得到如下代码:class Television /class关键字 类名 /边界public:/外部接口void SetChannel(int ChannelNum);/行为,代码成员void ShutDown();/行为,代码成员private:/特定的访问权限in
4、t CurrentChannel;/属性,数据成员;2023年12月18日星期一C+面向对象程序设计4.1.3 继承t继承是面向对象程序设计中的一种重要机制,该机制自动的将一个类中的操作和数据结构提供给另一个类,这使得程序员可以使用已有类的成分来建立新类。lC+语言中可以让你声明一个新类作为另一个类的派生。派生类(也叫子类)继承其父类的属性和操作。子类也声明了新的操作和属性,除去了一些不适合于自身用途的继承下来的操作和属性。这样,继承可以让你重用父类的代码,专注于子类代码的编写。2023年12月18日星期一C+面向对象程序设计4.1.3 继承t对地球上的各种生物进行抽象,将可以得到如下形式的继
5、承关系图:2023年12月18日星期一C+面向对象程序设计4.1.3 继承t在面向对象程序设计方法出现以前,在标准的C函数库中,基本上不能找到可重用的代码部件。如果一个程序员已经开发了一些程序,现在要开发一个新的程序,实际上不可能用到先前程序中的代码部件,通常这些部件都要修改。t继承可以使已存在的类在不用修改的情况下适应新的应用,掌握C+面向对象程序设计所有方面的关键就在于理解继承。2023年12月18日星期一C+面向对象程序设计4.1.4 多态t多态,是指类中具有相似功能的不同函数使用同一个名称来实现。l以绘图程序为例子,函数可以对一般图形进行操作,而不关心它们是圆、正方形还是三角形。所有的
6、图形都能被绘制、擦除和移动,所以这些函数能简单地发送消息给一个图形对象,而不考虑这个对象如何处理这个消息。tC+中,多态是通过虚函数来实现的。2023年12月18日星期一C+面向对象程序设计4.2 建立类与对象t4.2.1 类的声明与实现t4.2.2 类成员的访问控制t4.2.3 类的成员函数t4.2.4 对象的建立与使用2023年12月18日星期一C+面向对象程序设计4.2.1 类的声明与实现t面向对象程序设计方法的核心是类,利用它可以实现对数据和方法的封装,通过类的继承,能够实现对问题的深入抽象描述。t类相当于一种用户自定义的类型,它和前几章提到的基本类型,如浮点型、整形,有类似的特征。2
7、023年12月18日星期一C+面向对象程序设计4.2.1 类的声明与实现t这里还是以电视为例,声明一个类来描述电视。class Television public:void SetChannel(int ChannelNum);void ShutDown();void TurnOn();private:int CurrentChannel;bool IsOpen;2023年12月18日星期一C+面向对象程序设计4.2.1 类的声明与实现t在类的声明中只声明了函数的原形,函数的实现可以在类外定义:void Television:SetChannel(int ChannelNum)./这里为设置频
8、道相关代码CurrentChannel=ChannelNum;void Television:ShutDown()./这里为关闭电视相关代码IsOpen=false;2023年12月18日星期一C+面向对象程序设计4.2.1 类的声明与实现 void Television:TurnOn()./这里为打开电视相关代码IsOpen=true;这样,我们就完成了对Television类的声明。首先以class关键字声明类的名称;然后声明类的数据成员和函数成员,通过public、private等关键字来说名类的成员的访问控制属性,最后再给出成员函数的实现。2023年12月18日星期一C+面向对象程序设
9、计4.2.1 类的声明与实现t下面再以一个简单例子说明,如何在C+程序中使用一个定义好的类:t这样,我们完成了一次对Television类的使用。首先声明了Television类的变量,也就是完成了Television类的实例化;然后通过类的变量调用类中的成员函数完成相应功能。Television Tv;/Television类的实例化Tv.TurnOn();/调用成员函数打开电视Tv.SetChannel(8);/调用成员函数选择频道Tv.ShutDown();/调用成员函数关闭电视2023年12月18日星期一C+面向对象程序设计4.2.2 类成员的访问控制t在C+中可以通过设置成员的访问控
10、制属性来实现对类成员访问权限的控制。访问控制属性有以下三种:lpublic(公有类型)lprivate(私有类型)lprotected(保护类型)2023年12月18日星期一C+面向对象程序设计4.2.2 类成员的访问控制t公有类型用来声明类的外部接口。公有成员用public关键字声明,任何一个来自外部的访问都必须通过这种外部接口来进行。tprivate关键字后面声明的就是类的私有类型成员,如果私有成员紧接着类名称,私有关键字可以省略,也就是说在默认情况下类成员会被当作私有类型。只有本类的成员函数可以访问私有类型的成员,而类外的任何访问都是非法的。2023年12月18日星期一C+面向对象程序设
11、计4.2.2 类成员的访问控制tprotected关键字定义的保护类型的性质和私有类型的性质相似,其差别在于继承过程中对新类的影响不同。t加入类成员的访问控制附后,可以将电视类写成以下形式:2023年12月18日星期一C+面向对象程序设计4.2.2 类成员的访问控制 class Television public:void SetChannel(int ChannelNum);private:int CurrentChannel;public:void ShutDown();void TurnOn();private:bool IsOpen;2023年12月18日星期一C+面向对象程序设计4.
12、2.3 类的成员函数t1命名成员函数l例:成员函数的定义class Circlepublic:void SetRadius(float Radius)/成员函数 m_Radius=Radius;float GetCircumference()/成员函数 return 2*m_Radius*3.14159265;2023年12月18日星期一C+面向对象程序设计4.2.3 类的成员函数t2.类中定义的成员函数l上面的例子中,类定义的大括号所包含的2个成员函数是在类中定义。定义在类中的成员函数一般规模都比较小,只有15句语句,而且特别的switch语句不能够使用。它们一般即使没有明确用inline标
13、示,也被认为是内联函数。2023年12月18日星期一C+面向对象程序设计4.2.3 类的成员函数t3.类之后定义的成员函数l对于语句比较多的成员函数来说,直接把代码放在类定义中使用起来很不便。为了避免这种情况,C+允许在类声明的后面定义成员函数。l例:类外定义成员函数class Circlepublic:void SetRadius(float Radius);float GetCircumference();;2023年12月18日星期一C+面向对象程序设计4.2.3 类的成员函数 t与在类内部定义成员函数时不一样,在类外部定义时,成员函数名前要多加上一个类名。/类外定义的成员函数void
14、Circle:SetRadius(float Radius)m_Radius=Radius;float Circle:GetCircumference()return 2*m_Radius*3.14159265;2023年12月18日星期一C+面向对象程序设计4.2.3 类的成员函数t4.类成员函数的重载l类成员函数的重载方法与普通函数一样。但由于类名是成员函数名的一部分,所以一个类的成员函数以另一个类的成员函数即使同名,也不能认为是重载。2023年12月18日星期一C+面向对象程序设计4.2.4 对象的建立与使用t类是一种抽象机制,它描述了一类问题的共同属性和行为。在面向对象程序设计中,类的
15、对象也就是具有该类类型的某一特定实体。l例如,人类是一个类,每个不同的人都是人类的实例(instance),或称对象(object)。2023年12月18日星期一C+面向对象程序设计4.2.4 对象的建立与使用t声明一个类的实例(对象)和声明一个一般变量相同,例如:t声明了类及其对象,我们就可以访问对象的公有成员,例如:Circle c;c.PrintCircumference();2023年12月18日星期一C+面向对象程序设计4.3 构造函数和析构函数t4.3.1 构造函数t4.3.2 析构函数2023年12月18日星期一C+面向对象程序设计4.3.1 构造函数t对象的初始化可以由类中的一
16、个特殊成员函数来完成,即构造函数。l构造函数的作用是在对象被创建时用特定的方式构造对象,将对象初始化为一个特定的状态,使此对象具有区别于其它对象的特征。构造函数在对象被创建的时候由系统自动调用,它完成的是一个由一般类到具体对象的过程。2023年12月18日星期一C+面向对象程序设计4.3.1 构造函数t普通构造函数例子:class Person /类定义public:Person(char*lpname,char*lpmale,int personage,char*lpcareer);/声明构造函数;Person:Person(char*lpname,char*lpsex,int person
17、age,char*lpcareer)/实现Person类构造函数strcpy(name,lpname);strcpy(sex,lpsex);age=personage;strcpy(career,lpcareer);2023年12月18日星期一C+面向对象程序设计4.3.1 构造函数t拷贝构造函数l拷贝构造函数是一种特殊的构造函数,有普通构造函数的所有特性,其形参必须是本类对象的引用。其作用是使用一个已经建立好的对象去初始化一个新的同类对象。l程序员可以根据实际需要定义特定的拷贝构造函数来实现同类对象之间的数据传递。如果程序员没有定义类的拷贝构造函数,系统会自动生成一个默认拷贝构造函数,其作用
18、是把初始值对象的每个数据成员的值都复制到新建立的对象中。2023年12月18日星期一C+面向对象程序设计4.3.1 构造函数t拷贝构造函数的定义例子 class Rectpublic:Rect(Rect&r);/声明拷贝构造函数;Rect:Rect(Rect&r)/实现拷贝构造函数top=r.top;left=r.left;width=r.width;height=r.height;cout拷贝构造函数被调用endl;2023年12月18日星期一C+面向对象程序设计4.3.2 析构函数t析构函数的作用与构造函数几乎正好相反,它在对象删除前被自动调用,来完成一些清理工作,也就是一些扫尾工作。l析
19、构函数与构造函数一样也是类的一个公有函数成员,它也不能有返回值,它的名称必须由类名前面加“”构成。2023年12月18日星期一C+面向对象程序设计4.3.2 析构函数t析构函数例子class Rectpublic:Rect(int rtop,int rleft,int rwidth,int rheight);Rect(Rect&r);int GetArea();int GetPerimeter();Rect()/内联的析构函数声明;2023年12月18日星期一C+面向对象程序设计4.4 类的作用域和对象的生存期 t4.4.1 类的作用域t4.4.2 对象的生存期2023年12月18日星期一C+
20、面向对象程序设计4.4.1 类的作用域t作用域是一个标识符在程序正文中有效的区域。l在讨论类的作用域时,我们可以把类看成是一组有名成员的集合,除了个别特殊情况外,类作用域作用于特定的成员名。2023年12月18日星期一C+面向对象程序设计4.4.2 对象的生存期t对象从诞生到结束的这段时间就是它的生存期。t对象的生存期可以分为静态生存期和动态生存期两种。l静态生存期t如果对象生存期与程序生存期相同,我们称它具有静态生存期。在文件中声明的对象都是具有静态生存期的。如果要在函数内部的块作用域中声明具有静态生存期的对象,则要使用关键字static,例如下列语句声明的对象r便是具有静态生存期的对象。s
21、tatic Rect r;2023年12月18日星期一C+面向对象程序设计4.4.2 对象的生存期l动态生存期t动态生存期对象诞生于声明点,结束于该标识符作用域结束处。t动态生存期例子:Circle gc;/具有静态生存期,文件作用域void main()gc.SetRadius(7);/对象成员具有类作用域 gc.PrintCircumference();Circle c;/声明具有动态作用域的对象c c.SetRadius(5);/引用对象c c.PrintCircumference();2023年12月18日星期一C+面向对象程序设计4.5 类的静态成员t4.5.1 静态数据成员t4.5
22、.2 静态函数成员2023年12月18日星期一C+面向对象程序设计4.5.1 静态数据成员t类属性是描述类的所有对象同一特征的一个数据成员,对于任何对象实例,类属性的值是相同的。t说明静态数据成员的语句格式是:static 类型说明符 成员名;l用关键字static声明l该类的所有对象维护该成员的同一个拷贝l必须在类外定义和初始化,用“类名:标识符”通过类名对它进行访问来指明所属的类。2023年12月18日星期一C+面向对象程序设计4.5.2 静态函数成员t和静态数据成员一样,静态成员函数也属于整个类,由同一个类的所有对象共同维护,被这些对象所共享。t静态成员函数的定义:static 类型 函
23、数名(形参)函数体l公有静态成员函数,可以通过类名或对象名来调用;l一般的非静态成员函数只能通过对象名来调用。2023年12月18日星期一C+面向对象程序设计4.5.2 静态函数成员t静态成员函数可以直接访问该类的静态数据和函数成员;而访问非静态数据成员,必须通过参数传递方式得到对象名,然后通过对象名来访问。class Rectpublic:static void f(Rect r);private:int y;void Rect:f(Rect r)couty;/错误,不能访问ycoutr.y;/正确2023年12月18日星期一C+面向对象程序设计4.6 常类型t4.6.1 常引用t4.6.2
24、 常对象t4.6.3 用const修饰的类成员 2023年12月18日星期一C+面向对象程序设计4.6.1 常引用t如果在说明引用时加上const修饰符,被说明的引用为常引用。常引用所引用的对象是不能被更改的。如果用常引用做形参,便可防止对实参的意外修改。void show(const int&x);int main()int y=100;show(y);return 0;void show(const int&x)coutx”操作符。t通过指针访问对象成员对象指针名-成员名2023年12月18日星期一C+面向对象程序设计4.7.3 动态配置对象内存t在C+中我们可以通过动态内存分配技术,在程
25、序实际运行过程中按照实际需要申请适量的内存,使用结束后还可以释放,这种在程序运行过程中申请和释放内存单元的过程一般称为建立和删除。t建立和删除堆对象使用两个运算符:new和delete2023年12月18日星期一C+面向对象程序设计4.7.3 动态配置对象内存t运算符new的功能是动态分配内存,或者称为动态创建堆对象,例如:Rect*pRect;pRect=new Rect(0,0,100,100);t运算符delete用来删除由new建立的对象,释放指针所指向的内存空间,例如:delete pRect;2023年12月18日星期一C+面向对象程序设计4.7.4 浅拷贝和深拷贝t浅拷贝l实现对
26、象间数据元素的一一对应复制。t深拷贝l当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指的对象进行复制。2023年12月18日星期一C+面向对象程序设计4.7.5 this指针t隐含于每一个类的成员函数中的特殊指针。t明确地指出了成员函数当前所操作的数据所属的对象。l当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。2023年12月18日星期一C+面向对象程序设计4.8 类的组合t4.8.1 类的组合t4.8.2 前向引用声明2023年12月18日星期一C+面向对象程序设计
27、4.8.1 类的组合t组合的概念l类的组合描述的是一个类内嵌其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系。l当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象也将被自动创建。2023年12月18日星期一C+面向对象程序设计4.8.1 类的组合t类组合的构造函数设计l在创建对象时既要对本类的基本数据成员进行初始化,又要对内嵌对象成员初始化。l组合类构造函数的声明形式:类名:类名(形参表):内嵌对象1(参数),内嵌对象2(参数),.类的初始化 2023年12月18日星期一C+面向对象程序设计4.8.1 类的组合t类组合的构造函数调用l构造函数调用顺序:先调用内嵌对
28、象的构造函数(按内嵌时的声明顺序,先声明者先构造),然后调用本类的构造函数。(析构函数的调用顺序相反)l若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。2023年12月18日星期一C+面向对象程序设计4.8.2 前向引用声明t类应该先声明,后使用t如果遇到两个类相互引用的情况,需要在某个类的声明之前引用该类,则应进行前向引用声明。t前向引用声明是在引用未定义的类之前对该类进行声明,它只为程序引入一个标识符,但具体声明在其它地方。2023年12月18日星期一C+面向对象程序设计4.9 友元t4.9.1 友元函数t4.9.2 友元类2023年12月18日星期一C+面
29、向对象程序设计4.9.1 友元函数t友元的概念l友元提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。l友元是C+提供的一种破坏数据封装和数据隐藏的机制。通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。l可以使用友元函数和友元类。l为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。2023年12月18日星期一C+面向对象程序设计4.9.1 友元函数t友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员t作用:增加灵活性
30、,使程序员可以在封装和快速性方面做合理选择。t访问对象中的成员必须通过对象名。2023年12月18日星期一C+面向对象程序设计4.9.2 友元类t类也和函数一样,可以声明为另一个类的友元,这时称为友元类。t声明语法:将友元类名在另一个类中使用friend修饰说明。例如:class Point /Point类的成员声明friend class Rect;/声明Rect类为Point的友元类 2023年12月18日星期一C+面向对象程序设计4.9.2 友元类t关于友元,还有两点要注意:1.友元关系是不能传递的;2.友元关系是单向的:如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护
31、数据,但A类的成员函数却不能访问B类的私有、保护数据。2023年12月18日星期一C+面向对象程序设计项目设计3 面向对象程序设计中类的应用t1 设计题目面向对象程序设计中类的应用t2 设计概要要求掌握面向对象程序设计方法及类的应用 t3 系统分析 t4 功能模块设计2023年12月18日星期一C+面向对象程序设计3 系统分析t校园信息管理系统,其主要功能就是对在校人员的资料进行管理。t该系统应该能维护在校人员的基本资料,包括学生和教师。l学生的基本资料录入、查询、分析、删除或归档。l教师基本资料的记录、与其工作相关的信息的记录;计算每个教师每月的工作量。2023年12月18日星期一C+面向对
32、象程序设计4 功能模块设计tstudent类定义定义数据类型数据类型描述描述namechar数组学生姓名sexchar数组学生性别sclasschar数组学生所在班级addresschar数组学生家庭住址birthdaydate(自定义结构)出生日期majorchar数组所学专业coursescourse数组(自定义结构)所学课程sregisterdate(自定义结构)入学日期Show成员函数输出当前实例信息Read成员函数输入实例信息Save成员函数将实例存入文件Load成员函数将实例从文件读入2023年12月18日星期一C+面向对象程序设计4 功能模块设计tteacher类定义定义数据类型数据类型描述描述namechar数组教师姓名sexchar数组教师性别addresschar数组教师家庭住址birthdaydate(自定义结构)出生日期coursescourse数组(自定义结构)所教课程tregisterdate(自定义结构)到校日期Show成员函数输出当前实例信息Read成员函数输入实例信息Save成员函数将实例存入文件Load成员函数将实例从文件读入