C面向对象程序设计.ppt_第1页
C面向对象程序设计.ppt_第2页
C面向对象程序设计.ppt_第3页
C面向对象程序设计.ppt_第4页
C面向对象程序设计.ppt_第5页
已阅读5页,还剩148页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第 3 章 C+面向对象程序设计,与传统的面向过程的程序设计(POP, Procedure Oriented Programming)语言相比,C+语言的最大特征是支持面向对象程序设计OOP(Object Oriented Programming),它引入了类、继承、多态和重载等面向对象的新机制。通过本章的学习,使我们系统地介绍C+面向对象设计的基本概念和基本方法。,3.1 面向对象程序设计方法及特征,面向对象不只是一种程序设计方法,还是一种建立客观事物模型、分析复杂事物的思想方法,它是以人们通常描述现实世界的方法来描述要解决的问题。面向对象是目前成熟并流行的软件工程方法之一,主要包括面向对象

2、分析和面向对象程序设计。,结构化程序设计的特点: 是一种自上而下、逐步求精的模块化程序设计方法。 是一种面向过程程序设计方法,即一个程序是由多个过程(在C+中为函数)模块组成,过程之间通过函数参数和全局变量进行相互联系(算法 + 数据结构 = 程序)。 。 与非结构化程序相比,结构化程序在调试、可读性和可维护性等方面都有很大的改进。 代码重用性不高:以过程为中心设计新系统,除了一些标准函数,大部分代码都必须重新编写 由于软、硬件技术的不断发展和用户需求的变化,按照功能划分设计的系统模块容易发生变化,使得开发出来的模块的可维护性欠佳。 面向过程模式将数据与过程分离,若对某一数据结构做了修改,所有

3、处理数据的过程都必须重新修订,增加了很多的编程工作量。,3.1.1 结构化程序设计,3.1.2 面向对象程序设计,面向对象程序设计: 面向对象程序设计就是运用以对象作为基本元素的方法,用计算机语言描述并处理一个问题。 面向对象程序设计符合客观世界本身的特点和人们分析问题的思维方式。 什么是对象: 客观世界是由各种各样的事物组成,包括真实的事物和抽象的事物。例如,人、动物、汽车(真实的事物)和程序、直线(抽象的事物)等。 每一类事物都有自己特定的属性(如大小、形状、重量等)和行为(如生长、行走、转弯、运算等),人们通过研究事物的属性和行为而认识事物。,在计算机科学中,对象是系统中用来描述客观事物

4、的一个实体,它是用于构成系统的一个基本单位,而系统可以看作是由一系列相互作用的对象组成。 在程序设计领域,可以用如下公式表示: 对象 = 数据 + 作用于这些数据上的操作,为了描述属性(又称状态)和行为(又称操作、方法)相同的一类对象,引入了类(class)的概念。 类定义了同类对象的公共属性和行为。 类 = 数据结构 + 对数据进行操作的函数 对象是类的一个实例,例如,汽车是一个类,而行驶在公路上的一辆汽车则是一个对象。 对象和类的关系相当程序设计语言中变量和变量类型的关系。,什么是类:,OOP围绕现实世界的概念来组织模块,采用对象描述问题空间的实体,用程序代码模拟现实世界中的对象,使程序设

5、计过程更自然、更直观。 SP是以功能为中心来描述系统,而OOP是以数据为中心来描述系统。相对于功能而言,数据具有更强的稳定性。 OOP模拟了对象之间的通信。就象人们之间互通信息一样,对象之间也可以通过消息进行通信。这样,我们不必知道一个对象是怎样实现其行为的,只需通过对象提供的接口进行通信并使用对象所具有的行为功能。 OOP把一个复杂的问题分解成多个能够完成独立功能的对象(类),然后把这些对象组合起来去完成这个复杂的问题。 一个对象可由多个更小的对象组成,如汽车由发动机、传送系统和排气系统等组成。这些对象(类)可由不同的程序员来设计,可在不同程序中使用,就象一个汽车制造商使用许多零部件去组装一

6、辆汽车,而这些零部件可能不是自己生产的。 采用面向对象模式就象在流水线上工作,我们最终只需将多个零部件(已设计好的对象)按照一定关系组合成一个完整的系统。,面向对象程序设计的特点:,面向对象程序设计方法的基本特征,面向对象程序设计方法具有四个基本特征: 抽象性、封装性、继承性、多态性,1. 抽象 抽象是人类认识问题的最基本手段之一。抽象是指对具体问题(对象)进行概括,抽出一类对象的公共属性和行为并加以描述的过程。抽象包括数据抽象和行为抽象。,2. 封装 封装是把每个对象的数据(属性)和操作(行为)包装在一个类中。一旦定义了对象的属性和行为,则必须决定哪些属性和行为只用于表示内部状态,哪些属性和

7、行为在外部是可见的。 一般限制直接访问对象的属性,而应通过操作接口访问,这样使程序中模块之间关系更简单、数据更安全。对程序的修改也仅限于类的内部,使得由于修改程序所带来的影响局部化。,3. 继承 继承是指一个新类可以从现有的类派生而来。新类继承了现有类的特性,包括一些属性和行为,并且可以修改或增加新的属性和行为,使之适合具体的需要。 例如,所有的Windows应用程序都有一个窗口,它们可以看作都是从一个窗口类派生出来的,但有的应用程序用于文字处理,有的应用程序用于绘图,这是由于派生出了不同的类,它们增加了不同的属性和行为。 继承很好地解决了软件的可重用性问题。,4. 多态性 多态性是指类中具有

8、相似功能的不同函数使用同一个名称来实现,并允许不同类的对象对同一消息作出的响应不相同。 例如,同样的“编辑|粘贴”操作,在字处理程序和绘图程序中有不同的结果;同样的加法,把两个时间值相加和把两个整数相加的要求肯定不同。 多态性使程序设计灵活、抽象,具有行为共享和代码共享的优点,很好地解决了程序的函数同名问题。,学生类 private: 学号; 姓名; public: 信息设置; 听讲; ; 好学生类 public: 听讲认真; 完成作业; ;,主函数 学生类 s1; s1.信息设置; s1.听讲; 好学生类 s2; s2.信息设置; s2.听讲; s2.完成作业; 学生类 *ps; ps= ,

9、为了支持面向对象程序设计,C+在C语言结构(struct)数据类型的基础上引入了类这种抽象数据类型。 C+面向对象编程实质上就是面向类编程,只有定义和实现了类,才能声明属于这个类的对象,才能通过对象使用定义的成员。 传统C程序员把编程重点放在函数的编写上,而C+程序员把重点放在类的定义和实现上。,3.2 类与对象,struct Point intx; inty; void print() coutxy; ; void main() Point pt; pt.x=100; pt.y=100; pt.print(); ,class,public:,C+类将对象的属性抽象为数据成员,将对象的行为抽象

10、为成员函数,并对它们进行封装。 数据成员又称成员变量,成员函数又称为方法。 C+类在形式上类似于C语言中用户自定义的结构类型,但定义类时规定了成员的访问控制权限。对象只能访问所属类的公有成员,而类的私有成员只能在类的成员函数中被访问。,3.2.1 类的定义与实现,class private: ; public: ; protected: ; ;,C+类定义的基本形式,class Time private:/ 最好不要省略private int hour;/ 数据成员,表示小时 int minute;/ 数据成员,表示分钟 int second;/ 数据成员,表示秒 public: void s

11、etTime(int, int, int);/ 成员函数,设置时间 void showTime();/ 成员函数,输出时间 ;,例3_1:定义类Time(表示时间)。,私有数据成员hour、minute 和second只能在类的成员 函数中被访问或赋值;,公有成员函数setTime、showTime 可在外部被调用,但必须通过一个 对象作为对象的成员使用。,练习:P122 3-37,利用C+类进行面向对象编程,定义类的成员只是完成了工作的第一步,最重要的工作是实现定义的类。 类的实现实质上是类的成员函数的实现,即定义类的成员函数。 成员函数的定义形式与一般函数的定义形式基本相同,但必须在成员函

12、数名前加上类名和作用域限定符(:)。 成员函数的定义也可放在类体内(该函数声明之处),这时成员函数将变成内联函数。,类的实现,void Time:setTime(int h, int m, int s) hour=(h=0 ,例3_2:类Time的实现。,private成员hour、minute和second不允许 外界存取,所以为类Time增加两个public 成员函数,供外界设置或显示private成员。,对象是类的一个实例,定义并实现了类,就可以利用定义好的类来声明对象,即创建对象。声明对象的形式与声明普通变量类似,例如: Time t1, start ;Time *pt1=,利用类声明

13、对象,声明对象后,就可以通过成员运算符“ . ”或指向运算符“-”访问对象的公有成员,但不能访问对象的私有成员。 例如,公有成员函数调用: t1.setTime();start.showTime(); pt1-setTime(); 而任何形如t1.hour、t1.minute、start.second等私有成员变量的直接访问都是非法的。,成员的访问,void main() Time EndTime;/ 声明对象EndTime EndTime.setTime(12, 23, 36);/ 设置对象EndTime的时间 coutThe time is:; EndTime.showTime();/ 显

14、示对象EndTime的时间 ,例3_3:类Time的使用,声明对象并设置对象属性。,一般将类的定义放在头文件(.h)中,类的实现放在源文件(.cpp)中,而main主函数可以放在另一个源文件中。在源文件中用#include编译预处理指令包含头文件。,C+面向对象编程约定之一:,#include class Time private: int hour;/ 数据成员,表示小时 int minute;/ 数据成员,表示分钟 int second;/ 数据成员,表示秒 public: void setTime(int h, int m, int s) / 成员函数,设置时间 hour=(h=0 ,T

15、he time is:12 : 23 : 36,练习:P122 3-38,在定义类时不能对成员变量进行初始化,因为无法确定成员变量属于哪一个对象。 成员变量一般都定义为私有属性,也不能在声明对象后利用赋值运算对成员变量进行初始化。 成员变量的初始化一般是利用一个名为构造函数的成员函数来完成。,3.2.2 构造函数和析构函数,如何进行成员 变量的初始化?,?,构造函数是一种特殊的成员函数,它是在创建对象时(声明或new动态创建)系统自动调用的成员函数。 构造函数的作用就是在对象被创建时利用初始值去构造对象,使得在声明对象时就能自动地完成对象的初始化。,什么是构造函数:,不能指定任何返回值类型,包

16、括void返回类型,没有返回值 构造函数的名称与类名相同 构造函数可以有任意类型的参数,可重载 成员函数,可以在类体内实现,也可以在类体外实现 在创建对象时由系统自动调用,程序中不能直接调用,析构函数也是一种特殊的成员函数,它是在对象生存期结束时系统自动调用的成员函数。 析构函数的作用是在对象被删除前做一些清理工作和数据保存工作。 例如:利用delete运算释放用new运算分配的内存单元,在关闭一个Windows窗口时可以通过调用析构函数保存窗口中的内容。,什么是析构函数:,不能指定任何返回值类型,包括void返回类型,没有返回值 析构函数的名称在类名前加上“”符号 析构函数无参,不能重载,一

17、个类只能有一个析构函数 成员函数,可以在类体内实现,也可以在类体外实现 在删除对象时由系统自动调用,程序中不能直接调用,#include class Time private: int hour; int minute; int second; public: Time(int, int, int); / 构造函数 Time(); / 析构函数 . . . . . . ;,例3_4:为类Time添加构造函数和析构函数。,Time:Time(int h, int m, int s) hour=h; minute=m; second=s; / 对私有成员变量初始化 coutThe construc

18、tor be called:hour:minute : secondendl; ,Time:Time() coutThe destructor be called:hour:minute :secondendl; ,功能与成员函数 Time:setTime()类似,void main(void) Time t1(10, 35, 55) ; / 自动调用构造函数 Time t2(16, 53, 9) ; / 自动调用构造函数 / 退出main()主函数时自动调用析构函数,构造函数和析构函数的自动调用:,The constructor be called:10:35:55 The construc

19、tor be called:16:53:9 The destructor be called:16:53:9 The destructor be called:10:35:55,为什么是 这个结果?,?,结果分析:,当创建一个对象时,系统先根据类定义的成员变量为对象分配内存空间,然后自动调用对象的构造函数对这段内存空间进行初始化处理,从而完成对象的初始化。,当撤消一个对象时,系统先自动调用对象的析构函数,然后释放对象所占内存空间。 从程序的运行结果可以看出,析构函数的调用顺序一般与构造函数的调用顺序相反。 栈:后进先出,与一般数据类型的变量相比,对象在它的生存期会有大量的操作,有时这些操作的结

20、果必须在对象的生存期结束时加以清理。因此可以在析构函数中进行动态分配的内存清理工作。 如果定义类时没有提供构造函数和析构函数,编译系统将会自动为类分别添加一个缺省的构造函数和析构函数。 如果用户加上自定义的构造函数和析构函数,编译系统将不会再添加缺省的构造函数和析构函数。,补充说明:,类名();/缺省的构造函数 类名(参数名);/参数形式的构造函数 类名(const 类名 public: A( )a=b=0;coutA1n; A(int m)a=b=m;coutA2n; A(int m,int n)a=m;b=n;coutA3n; void print()coutabendl; A()cout

21、abendl; ; void main() A a1,a2(10),a3(10,20); a1.print(); a2.print(); a3.print(); ,3.2.3 静态成员,静态成员的概念: 一般情况下,同一个类不同对象的数据成员所占用的内存空间是不同的(体现了不同对象具有不同的属性值)。在有些情况下,类的数据成员的值对每个对象都是相同的(类属性),如当前已创建对象的数量,这时可以将该成员声明为静态成员。 静态成员分为静态数据成员和静态成员函数。,静态数据成员: 静态数据成员类似于一般的static静态变量,它具有全局性。静态数据成员属于整个类,为类的所有对象共享。 无论类的对象有

22、多少,类的静态数据成员只有一份,存储在同一个内存空间。即使没有创建类的一个对象,类的静态数据成员也是存在的。 使用静态数据成员保证了该数据成员值的唯一性。,静态数据成员的访问: 公有静态成员:三种方式 (1)通过对象访问,如:person1.m_nCount=100; (2)利用类名和作用域限定符(:)访问,如: int Person:m_nCount=100; / 初始化 (3)在成员函数中访问,如:m_nCount+; 私有和保护静态成员:只能在成员函数中访问,静态数据成员的声明: 在声明成员时以关键字static开头,例如: static int m_nCount;,静态成员的初始化:

23、放在类定义的外部,格式为: 数据类型 类名:静态数据成员=值 例如:int Person : m_nCount = 0;,静态成员函数:,有时需要在声明对象之前访问私有静态数据成员,这时必须定义一个访问静态数据成员的静态成员函数。 声明方式与静态成员变量类似。 public: static int GetCount(); 静态成员函数也与一个类相关联,而不只与一个特定的对象相关联。 区别非静态成员函数,静态成员函数没有this指针,因为类的静态成员函数只有一个运行实例。 成员函数一般是公有属性,可以通过对象、类名和作用域限定符、在成员函数中三种方式调用静态成员函数。 person1.GetCo

24、unt( ); Person:GetCount( ); 在Person成员函数中直接调用GetCount( );,静态成员函数只能访问类的静态成员(成员变量和成员函数),而不能访问类的非静态成员。因为当通过类名和运算符“:”调用一个静态成员函数时,不能确定函数中所访问的非静态成员属于哪一个对象。 解决方法:将对象作为静态成员函数的参数,然后在静态成员函数中通过对象访问它的非静态成员。,注 意,例3_5:静态成员变量和静态成员函数的使用。,#include #include class Person public: char m_strName20; long m_ID;,static int

25、m_nCount; / 静态成员变量,表示已创建对象的数量 public: Person(char*, long);/ 构造函数 static int GetCount();/ 静态成员函数 static long GetID(Person); / 对象作为静态成员函数的参数 ; Person:Person(char* strName, long ID) strcpy(m_strName, strName); m_ID=ID; m_nCount+;/ 对象数目加1 int Person:GetCount() return m_nCount;/ 访问静态成员变量 long Person:GetI

26、D(Person x) return x.m_ID; / 不能直接访问非静态成员m_ID,int Person:m_nCount=0;/ 初始化静态成员变量 void main() Person e1(LiuJun,1101051); coutPerson:m_nCount,e1.m_nCount n; / 通过类或对象访问静态成员变量 coutPerson:GetCount(),Person:GetID(e1)n; / 通过类调用静态成员函数 coute1.GetCount(),e1.GetID(e1)n; / 通过对象调用静态成员函数 Person e2(WangXiaogang,1101

27、058); coutPerson:GetCount(),Person:GetID(e2)n; coute2.GetCount(),e2.GetID(e2)n; coute1.GetCount(),e1.GetID(e1)n; / e1和e2共享静态成员变量m_nCount ,程序运行结果为: 1,1 1,1101051 1,1101051 2,1101058 2,1101058 2,1101051,练习:P123 3-39(1),3.2.4 this指针,每个成员函数都隐藏有一个名为this指针的参数,它是一个特殊的指针,指向调用成员函数的对象。 当通过一个对象调用成员函数(非静态成员函数)时

28、,编译器要把对象的地址传递给this指针。 在成员函数中访问数据成员或调用其他成员函数不需要指定对象,因为它们都是通过一个隐藏的this指针确定当前的对象。,下面定义的成员函数并没有声明this参数:,void Time:showTime() couthour:minute:secondendl;,编译器会把this指针作为成员函数的参数:,void Time:showTime(Time* this); couthourminutesecondendl;,调用时:,当程序中调用某个成员函数时,编译器会把该对象的地址赋值给this指针,并将该地址值加入到参数表中,如下所示: EndTime.sh

29、owTime(,作用:,在一个成员函数中经常需要调用其它函数(非本类的成员函数),而有时需要把对象本身(即对象的地址)作为参数传递给被调用函数,这时必须使用this指针。,例3_6:this指针的使用。,#include #include class Person public: / 可在外部直接访问public属性的数据成员 char m_strName20;,char m_ID18; public: Person(char* strName, char* ID) / 内联构造函数 strcpy(m_strName, strName); strcpy(m_ID, ID); void Show

30、(); ; void Display(Person* pObj)/ 非成员函数 coutm_strNamem_IDShow(); / 通过调用Show调用Display ,this指针在windows编程中相当有用。当调用其他类的成员函数时,如果想得到主调函数的对象的句柄(如windows中的窗口、控件、设备或文件等),可以使用this指针。 void CMyView:OnLButtonDown(UINT nFlags,CPoint point) CClientDC dc(this); dc.LineTo(point); ,3.2.5 友元,类具有封装性,类的私有成员一般只能通过该类的成员函数

31、访问,这种封装性隐藏了对象的数据成员,保证了对象的安全,但有时带来了编程的不方便。,友元函数: C+提供了一种函数,它虽然不是一个类的成员函数,但可以像成员函数一样访问该类的所有成员,包括私有成员和保护成员。这种函数称为友元(friend)函数。,友元函数的声明:,一个函数要成为一个类的友员函数,需要在类的定义中声明该函数,并在函数声明的前面加上关键字friend。,友元函数本身的定义没有什么特殊要求,可以是一般函数,也可以是另一个类的成员函数。 为了能够在友元函数中访问或修改类的私有数据成员,一个类的友元函数一般将该类的对象或引用作为函数参数。,class A friend void dis

32、play(A); / 友元函数是一个一般函数 friend void B:BMemberFun(A / 友元函数是另一个类B的成员函数 public: . . . ,友元类: 友元的另一种类型是友元类,一个类可以声明另一个类为其友元类,这个友元类的所有成员函数都可以访问声明其为友元的类的所有成员。 class A friend C; ; 由于访问权限控制符不影响友元声明,友元声明可放在类体中任何地方,建议把友元声明放在类体的开始位置。,说明: 友元关系是单方向的,不具有交换性和传递性。 使用友元虽然简化了编程,并可避免调用成员函数的开销,但破坏了类的封装性,建议谨慎使用。,#include c

33、lass A; class B public: void BMemberFun(A,public: A(int x=0,int y=0)a=x;b=y; ; void display(A e) couta=e.a, ; coutb=e.bendl; void B:BMemberFun(A ,例3_7:友元的声明和使用,3.2.6 常对象和常对象成员,类的封装保证了数据的安全性,但各种形式的数据共享(如静态成员和友元)却有不同程度的破坏了数据的安全性。对于既需要共享又需要安全的数据,可以利用const来进行保护。 const还可用来修饰对象(常对象)、数据成员(常数据成员)和成员函数(常成员函数

34、)。,1、const常对象,常对象的数据成员的值在对象的整个生存期内不能被改变,必须利用构造函数进行初始化,且以后不能再被更新。,const (初始值列表) const (初始值列表),const Time meeting ( 8, 30, 00 );,#include class Time private: int hour,minute,second; public: Time(int h,int m,int s) hour=h;minute=m;second=s; void changeTime(int h,int m,int s) hour=h;minute=m;second=s; ;

35、 void main(void) Time const meeting(8,30,00); meeting.changeTime(9,00,00); ,例3_8:分析以下程序有什么错误。,2、常成员函数,可以使用const关键字限制成员函数对数据成员进行修改工作,这种使用const关键字进行声明的成员函数称为常成员函数。, () const;,举例:void MemberFun( ) const;,由于常成员函数只能访问数据成员,不能设置或修改数据成员,因此,在常成员函数中只能调用常成员函数,而调用其他普通函数可能会造成间接修改数据成员。 同理,通过常对象只能调用常成员函数,不能调用普通成员函

36、数(构造函数除外),但通过普通对象可以调用常成员函数。,不修改对象中数据成员的函数可声明为常成员函数,常成员函数的声明格式如下:,#include class Time private: int hour,minute,second; public: Time(int h,int m,int s) hour=h;minute=m;second=s; void ShowTime() const;/使用const声明常成员函数 ; void Time:ShowTime() const couthour:minute:secondendl; void main(void) Time const me

37、eting(8,30,00); meeting.ShowTime();/常对象只能调用常成员函数 Time classTime(8,10,00); classTime.ShowTime();/普通对象可以调用常成员函数 ,例3_9:常对象和常成员函数的使用。,#include class Time private: int hour,minute,second; public: Time( ) Time(int h,int m,int s) hour=h; minute=m; second=s; void settime(int h,int m,int s) hour=h; minute=m;

38、 second=s; int gethour( ) const return hour; int getminute( ) const return minute; int getsecond( ) const return second; void print( ) const couthour:minute:secondendl; ; void main(void) Time t1;const Time t2(10,23,34); t1.settime(11,15,20);t2.settime(4,12,15); t1.print();t2.print();,3、常数据成员,如果某个数据成

39、员的值是不应该被修改的,那么可以将其声明为const,使其受到强制保护,以防被意外改动。 const 数据类型 变量名; 对于常数据成员,任何函数都不能对它进行赋值。 常数据成员的初始值只能通过构造函数获取,并且只能在构造函数的初始化列表中设置其初始值。 Sample ( int x, int y ) : b ( y ) 说明:对非常数据成员也可以用初始化列表进行初始化。 Sample ( int x, int y ) : b ( y ),a ( x ) 静态常数据成员的初始化只能在类体外进行。 const int Sample : : c=100;,#include class Sample

40、 private: int a; const int b; static const int c; public: Sample(int x,int y):b(y) a=x; void Display( ) cout“a=”a“,b=”b“,c=”cendl; ; const int Sample:c=100; void main() Sample MyObject1(5,10); MyObject1.Display(); Sample MyObject2(8,12); MyObject2.Display();,例3_10:常数据成员的声明和使用。,练习:P123 3-39(3),P124:3

41、-39(3)以下程序有什么错误?如有请予以修改。 #include class Sample private: int n; public: Sample(int x) n=x; void SetValue(int x) n=x; void Display() coutn=nendl; ; void main() const Sample a(100); a.SetValue(0); a.Display(); ,习题50、52:建立一个名为Student的类,该类有以下几个私有成员变量:学生姓名、学号、性别和年龄。还有以下两个成员函数:一个用于初始化学生姓名、学号、性别和年龄的构造函数,一个用

42、于输出学生信息的函数。编写一个主函数,声明一个学生对象,然后调用成员函数在屏幕输出学生信息。添加一个静态成员变量用于表示已创建对象的数量;添加两个静态成员函数,一个用于输出已创建对象的数量,一个用于输出一个学生的姓名和学号。,#include #include class Student char m_strName20; char m_ID20; char m_sex; int m_age; public: Student(char*,char*,char,int); void PrintMsg(); ;,Student:Student(char* strName,char* ID,char

43、 sex,int age) strcpy (m_strName,strName); strcpy (m_ID,ID); m_sex=sex; m_age=age; void Student:PrintMsg() cout学生姓名:m_strName 学号:m_ID性别:; if(m_sex=M) cout男; else cout女; cout 年龄:m_ageendl; void main() Student s(LiMing,110121112119,M,20); s.PrintMsg(); ,#include #include class Student char m_strName20;

44、 char m_ID20; char m_sex; int m_age; public: static int mCount; Student(char*,char*,char,int); void PrintMsg(); static void PrintCount(); static void PrintNameID(Student); ; int Student:mCount=0; Student:Student(char* strName,char* ID,char sex,int age) strcpy (m_strName,strName); strcpy (m_ID,ID); m

45、_sex=sex; m_age=age; mCount+;,void Student:PrintMsg() cout学生姓名:m_strName 学号:m_ID 性别:; if(m_sex=M)cout男; elsecout女; cout 年龄:m_ageendl; void Student:PrintCount() cout当前学生人数为:mCountendl; void Student:PrintNameID(Student x) cout学生姓名:x.m_strName 学号:x.m_IDendl; void main() Student s1(LiMing,110121112119,M

46、,20); s1.PrintMsg(); Student s2(LiuJun,110121112120,N,19); s2.PrintMsg(); Student:PrintCount(); s1.PrintCount(); Student:PrintNameID(s2); ,3.3 继承与派生,继承是面向对象程序设计方法的四个基本特征之一,是程序代码可重用性的具体体现。 在C+面向对象程序设计中,所谓类的继承就是利用现有的类创建一个新的类。新类继承了现有类的属性和行为。 为了使新类具有自己所需的功能,它可以扩充和完善现有类的属性和行为,使之更具体。,继承,发扬,微软基础类MFC就是通过类的继

47、承来体现类的可重用性和可扩充性。,3.3.1 基类和派生类,在现实世界中,一类事物的对象常常也属于另一类事物。 在面向对象程序设计方法中,一个类的对象也常常是另一个类的对象,即一个类具有了另一个类的属性和方法。 在定义一个类时,根据类的继承性,我们能够且应尽可能地利用现有的类来定制新的类,而不必重新设计新的类。,1. 问题的提出,2. 基类和派生类的概念,在继承关系中,新定义的类称为原有类的派生类或子类,而原有的类称为新定义类的基类或父类。派生类继承了基类的所有属性和行为,并且派生类可以对基类的行为进行修改和完善,还可以增加新的属性和行为。 一个派生类也可以作为另一个派生类的基类。,class

48、 : . . . / 派生类新增加的成员声明列表 ;,3. 派生类的定义,派生方式决定了基类的成员在派生类中的访问权限。派生方式共有三种:public、private和protected(缺省值为private)。 虽然派生类继承了基类的所有成员,但为了不破坏基类的封装性,无论采用哪种派生方式,基类的私有成员在派生类中都是不可见的,即不允许在派生类的成员函数中访问基类的私有成员。,说明:,派生类成员的类型由继承方式和基类类型共同确定,采用public派生,基类成员的访问权限在派生类中保持不变,即基类所有的公有或保护成员在派生类中仍为公有或保护成员。public派生最常用。 (1) 可以在派生类

49、的成员函数中访问基类的非私有成员; (2) 可通过派生类的对象直接访问基类的公有成员。 采用private私有派生,基类所有的公有和保护成员在派生类中都成为私有成员,只允许在派生类的成员函数中访问基类的非私有成员。private派生很少使用。 采用protected保护派生,基类所有的公有和保护成员在派生类中都成为保护成员,只允许在派生类的成员函数和该派生类的派生类的成员函数中访问基类的非私有成员。,三种派生方式的区别:,例3-11:定义类Point(表示点),然后定义类Point的派生类Circle(表示圆)。,#include class Point/ 定义基类,表示点 private:

50、int x; int y; public: void setPoint(int a, int b) x=a; y=b; ; / 设置坐标 int getX() return x; ;/ 取得X坐标 int getY() return y; ;/ 取得Y坐标 ;,class Circle : public Point / 定义派生类,表示圆 private: int radius; public: void setRadius(int r) radius=r; ; / 设置半径 int getRadius() return radius; ; / 取得半径 int getUpperLeftX()

51、 return getX() - radius; ; / 取得外接正方形左上角的X坐标 int getUpperLeftY() return getY() + radius; ; / 取得外接正方形左上角的Y坐标 ;,void main() Circle c; c.setPoint(200, 250); c.setRadius(100); coutX=c.getX(), Y=c.getY() , Radius=c.getRadius()endl; coutUpperLeft X=c.getUpperLeftX() , UpperLeft Y=c.getUpperLeftY()endl; ,公有

52、派生类的对象可以直接访问基类Point的公有成员,程序运行结果:X=200,Y=250,Radius=100UpperLeft X=100,UpperLeft Y=350,派生类Circle通过public派生方式继承了基类Point的所有成员(除私有成员外所有成员的访问权限不变),同时还定义了自己的成员变量和成员函数。 若将类Circle的派生方式改为private或protected,则下述语句是非法的:c.setPoint(200, 250);,说明:,!,容易 混淆,无论哪种派生方式,派生类都继承了基类的所有成员,包括私有成员。我们虽然不能在派生类Circle中直接访问私有数据成员x和

53、y,但可以通过继承的公有成员函数getX()、getY()和setPoint()访问或设置它们。,利用类继承定义类可能带来一个问题:派生类会继承它不需要的基类中的数据成员和成员函数,这时,基类中不适合于派生类的成员可以在派生类中重新加以定义。,问题:,?,例3-12:派生类成员函数对基类成员函数的覆盖。,#include class A public: void Show( ) coutA:Shown; ; ;,class B : public A public: void Show( ) coutB:Shown; ; / 在派生类中重新定义成员函数 void Display() Show(

54、); ; / 调用派生类B的成员函数Show() ; void main() A a; B b; a.Show();/ 调用基类A的成员函数Show() b.Show();/ 调用派生类B的成员函数Show() b.Display(); ,如果想调用基类A的成员函数Show(),可以使用作用域限定符“:”:A: Show();,程序运行结果: A:Show B:Show B:Show,从本例可以看出,虽然派生类继承了基类的所有成员函数,但如果派生类某个成员函数的名称和参数与基类成员函数一致(即在派生类中对该成员函数重新进行了定义),则在派生类中调用的成员函数是派生类的成员函数。 请问:如果在派

55、生类B中没有对成员函数Show()重新进行定义,程序运行结果如何?,?,为什么我们经常在现有类的基础上采用继承的方法来定制新类,而不通过直接修改现有类来设计自己的类?除了代码重用的优越性,其主要原因是可能得不到基类的实现源码。,继承的 重要性!,在利用微软基础类MFC派生自己的类时,我们只需要MFC类声明的头文件(利用#include指令将头文件包含)和含有成员函数目标代码的OBJ文件,并不需要整个MFC类库的实现源码。,3.3.2 派生类的构造函数和析构函数,一个派生类对象也属于其基类,因此当程序创建一个派生类对象时,系统首先自动创建一个基类对象。 基类的构造函数不能被继承,派生类的构造函数

56、必须调用基类的构造函数来初始化基类的子对象。 基类的析构函数也不能被继承,执行派生类的析构函数时,先执行派生类的析构函数,在执行基类的析构函数。 在调用派生类的构造函数构建派生类对象时,系统首先调用基类的构造函数构建基类对象。当派生类对象的生存期结束时,首先调用派生类的析构函数,然后调用基类的析构函数。,1. 问题的提出,编译器在对程序编译时,首先生成基类构造函数的调用代码,然后生成派生类构造函数的调用代码。,2. 基类构造函数的调用方式,隐式调用和显式调用两种方式:,(1)隐式方式是指在派生类的构造函数中不指定对应的基类的构造函数,调用的是基类的默认构造函数。,#include class

57、A public: A()coutAn; ; class B:public A public: B()coutBn; ;,void main() B b; ,注意:除非基类有默认的构造函数,否则必须采用显式调用方式。,(2)显式方式是指在派生类的构造函数中指定要调用的基类构造函数,并将派生类构造函数的部分参数值传递给基类构造函数。,!,3. 显式方式构造函数的定义,设类B是类A的派生类,则派生类B显式方式构造函数的定义形式如下:,B:B( ) : A( ) . . . / 类B构造函数的实现代码 ,派生类构造函数既初始化派生类的数据成员,又通过基类构造函数初始化其基类的数据成员。 参数表中参数的个数和类型要与基类某个构造函数的形参声明一致。,形参声明中的部分参数,传递给基类构造函数,派生类构造函数形参的名称和类型,举例: Point ( int a, int b ) x=a; y=b; ; Circle ( int m, int n, int r ) : Point (m, n) radius=r; ; Circle c ( 100 , 200 , 300 );,注意:当基类有多个构造函数时,编译器根据派生类构造函数为基类构造函数提供的参数表来确定调用基类的哪一个构造函数。,Point

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论