C 程序设计课件:第八章 继承与多态_第1页
C 程序设计课件:第八章 继承与多态_第2页
C 程序设计课件:第八章 继承与多态_第3页
C 程序设计课件:第八章 继承与多态_第4页
C 程序设计课件:第八章 继承与多态_第5页
已阅读5页,还剩111页未读 继续免费阅读

下载本文档

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

文档简介

1、继承(inheritance):该机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构。体现了由简单到复杂的认识过程。第八章 继承与多态多态性(polymorphism):多态性包括静态的多态性和动态的多态性。前者亦称编译时的多态性,包括函数的重载和运算符的重载。后者亦称运行时的多态性,这是以虚函数为基础的,是面向对象程序设计的标志性特征。 体现了类推和比喻的思想方法。 第八章 继承与多态8.1 继承与派生的概念 8.4 虚基类 (选读) 8.3 多重继承与派生类成员标识 (选

2、读) 8. 6 多态性与虚函数 8.5 派生类应用讨论 8.2 派生类的构造函数与析构函数 附录: UML类图中的依赖关系附录:用UML类图表示派生8.1 继承与派生的概念层次概念是计算机的重要概念。通过继承(inheritance)的机制可对类(class)分层,提供类型/子类型的关系。 C+通过类派生(class derivation)的机制来支持继承。被继承的类称为基类(base class)或超类(superclass),新的类为派生类(derived class)或子类(subclass)。基类和派生类的集合称作类继承层次结构(hierarchy)。 如果基类和派生类共享相同的公有接

3、口,则派生类被称作基类的子类型(subtype)。 层次概念:派生反映了事物之间的联系,事物的共性与个性之间的关系。 派生与独立设计若干相关的类,前者工作量少,重复的部分可以从基类继承来,不需要单独编程。 8.1 继承与派生的概念8.1.1 类的派生与继承 8. 1.2 公有派生与私有派生 派生类的定义:class 派生类名:访问限定符 基类名1,访问限定符 基类名2,访问限定符 基类名n private: 成员表1; /派生类增加或替代的私有成员public:成员表2; /派生类增加或替代的公有成员protected:成员表3; /派生类增加或替代的保护成员;/分号不可少其中基类1,基类2,

4、是已声明的类。 在派生类定义的类体中给出的成员称为派生类成员,它们是新增加成员,它们给派生类添加了不同于基类的新的属性和功能。派生类成员也包括取代基类成员的更新成员。8.1.1 类的派生与继承8.1.1 类的派生与继承访问限定符两方面含义:派生类成员(新增成员)函数对基类(继承来的)成员的访问(调用和操作),和从派生类对象之外对派生类对象中的基类成员的访问。放在后面讨论。公有派生限制最少,是派生的主流。访问限定符:基类名前的访问限定符,是对基类成员进一步的限制。访问控制也是三种:公有(public)方式,亦称公有继承保护(protected)方式,亦称保护继承私有(private)方式, 亦称

5、私有继承。 基类1基类2基类n派生类1派生类2基类派生类1派生类2(a)多重继承 (b)单继承 图8.1 多重继承与单继承 一个基类可以直接派生出多个派生类 派生类可以由多个基类共同派生出来,称多重继承。8.1.1 类的派生与继承多重继承:如果一个派生类可以同时有多个基类,称为多重继承(multiple-inheritance),这时的派生类同时得到了多个已有类的特征。单继承:派生类只有一个直接基类的情况称为单继承(single-inheritance)。8.1.1 类的派生与继承 在派生过程中,派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构。直接参与派生出某类称

6、为直接基类,而基类的基类,以及更深层的基类称为间接基类。类族: 同时一个基类可以直接派生出多个派生类。这样形成了一个相互关联的类族。如MFC就是这样的族类,它由一个CObject类派生出200个MFC类中的绝大多数。 多层次继承:编制派生类时可分四步 吸收基类的成员 改造基类成员 发展新成员 重写构造函数与析构函数 8.1.1 类的派生与继承不论是数据成员,还是函数成员,除构造函数与析构函数外全盘接收 声明一个和某基类成员同名的新成员,派生类中的新成员就屏蔽了基类同名成员派生类新成员必须与基类成员不同名,它的加入保证派生类在功能上有所发展。 派生编程步骤:8.1.1 类的派生与继承第二步中,新

7、成员如是成员函数,参数表和返回值也完全一样,称为同名覆盖(Override),否则是重载 。第三步中,独有的新成员才是继承与派生的核心特征。第四步是重写构造函数与析构函数,派生类不继承这两种函数。不管原来的函数是否可用一律重写可免出错。方式类似聚合含成员对象的类的构造函数。详细内容后文讨论。【例8.1】由在册人员类公有派生学生类【例8.1】由在册人员类公有派生学生类。我们希望基类和派生类共享相同的公有接口,只能采用公有派生来实现。基类:class Personstring IdPerson;/身份证号,18位数字string Name;/姓名Tsex Sex; /性别enum Tsexmid,

8、man,woman;int Birthday;/生日,格式1986年8月18日写作19860818string HomeAddress;/家庭地址public:Person(string, string,Tsex,int, string);/构造函数 Person(); /默认的构造函数 Person(); /析构函数【例8.1】由在册人员类公有派生学生类void SetName(string); /修改名字string GetName()return Name; /提取名字void SetSex(Tsex sex)Sex=sex; /修改性别Tsex GetSex()return Sex;

9、/提取性别void SetId(string id)IdPerson=id;/修改身份证号string GetId()return IdPerson; /提取身份证号void SetBirth(int birthday)Birthday=birthday; /修改生日int GetBirth()return Birthday; /提取生日void SetHomeAdd(string ); /修改住址string GetHomeAdd()return HomeAddress; /提取住址void PrintPersonInfo(); /输出个人信息;/接口函数:【例8.1】由在册人员类公有派生学

10、生类派生的学生类:class Student:public Person /定义派生的学生类 string NoStudent; /学号 course cs30; /30门课程与成绩public: Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud);/注意派生类构造函数声明方式 Student(); /默认派生类构造函数 Student(); /派生类析构函数 SetCourse(string ,int); /课程设置 int GetCourse(string ); /查找成绩

11、 void PrintStudentInfo(); /打印学生情况;struct course string coursename; int grade;验证主函数8.2 派生类的构造函数与析构函数注意: 本例中标准C+字符串string是作为成员对象使用的(聚合),动态内存分配的构造和析构被封装起来,使用十分简单。如使用动态生成的C风格字符串,要考虑深复制,那要复杂得多。 提倡完善的类对象封装,不仅封装数据和对数据的操作,而且封装资源的动态分配与释放,形成一个完备的子系统。在一个有层次结构的类体系中资源的动态分配与释放应封装在成员对象中,如同使用标准的string字符串类那样。 聚合是一种完

12、善的封装。采用成员对象将大大简化层次结构的类体系中资源的动态分配与释放的处理方法,不再出现难度极大的多层次的深复制。8.1.2 公有派生与私有派生访问限定符讨论:派生类成员(新增成员)函数对基类(继承来的)成员的访问(调用和操作),和从派生类对象之外对派生类对象中的基类成员的访问。下面进行详细讨论:8.1.2 公有派生与私有派生不可直接访问 不可直接访问 private 不可直接访问 private protected 不可直接访问 private public 私有派生所有接口必须重写 不可直接访问 不可直接访问 private 不可直接访问 protected protected 可直接访

13、问 public public 公有派生所有接口均可使用 在派生类对象外访问派生类对象的基类成员 在派生类中对基类成员的访问限定 基类中的访问限定 派生方式 公有派生是绝对主流。 8.1.2 公有派生与私有派生保护派生:直接派生: 基类的私有成员仍是派生类的私有成员,不可直接访问,而基类中的公有和保护成员全部成为派生类的保护成员,在派生类中可以直接访问。但在派生类对象之外是不能直接访问该对象的任何基类成员的。与私有派生相同。多层派生:把保护派生类作为基类或把私有派生类作为基类再作一层保护派生。在新的保护派生类中可直接访问由保护派生传递过来的底层基类的公有和保护成员,而不可直接访问由私有派生传递

14、来的底层基类的公有和保护成员。但在该类对象之外都不可直接访问类对象底层基类的公有成员。合理使用保护限定方式可以在复杂的类层次关系中取一个共享访问和成员封装隐蔽性的折衷。派生类构造函数的定义:派生类名:派生类名(参数总表):基类名1(参数名表1),基类名2(参数名表2),基类名n(参数名表n),成员对象名1(成员对象参数名表1),成员对象名m(成员对象参数名表m)/派生类新增成员的初始化; /所列出的成员对象名全部为新增成员对象的名字注意: 在构造函数的声明中,冒号及冒号以后部分必须略去。 所谓不能继承并不是不能利用,而是把基类的构造函数作为新的构造函数的一部分,或者讲调用基类的构造函数。基类名

15、仅指直接基类,写了底层基类,编译器认为出错。 冒号后的基类名,成员对象名的次序可以随意,这里的次序与调用次序无关。 8.2 派生类的构造函数与析构函数派生类构造函数各部分执行次序: 1.调用基类构造函数,按它们在派生类定义的先后顺序,顺序调用。 2.调用成员对象的构造函数,按它们在类定义中声明的先后顺序,顺序调用。3.派生类的构造函数体中的操作。8.2 派生类的构造函数与析构函数注意: 在派生类构造函数中,只要基类不是使用无参的默认构造函数都要显式给出基类名和参数表。 如果基类没有定义构造函数,则派生类也可以不定义,全部采用系统给定的默认构造函数。 如果基类定义了带有形参表的构造函数时,派生类

16、就应当定义构造函数。8.2 派生类的构造函数与析构函数析构函数:析构函数的功能是作善后工作。 只要在函数体内把派生类新增的一般成员处理好就可以了,而对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。 析构函数各部分执行次序与构造函数相反,首先对派生类新增一般成员析构,然后对新增对象成员析构,最后对基类成员析构。8.3 多重继承与派生类成员标识(选读)由多个基类共同派生出新的派生类,这样的继承结构被称为多重继承或多继承(multiple-inheritance) 椅子床沙发(单继承)躺椅(多重继承)两用沙发(多重继承)图8.2 椅子,床到两用沙发多重继承实例:在册人

17、员学生(单继承)教职工(单继承)兼职教师(单继承)教师(单继承)行政人员(单继承)工人(单继承)研究生(单继承)行政人员兼教师(多重继承)在职研究生(多重继承)研究生助教(多重继承)图8.3 大学在册人员继承关系8.3 多重继承与派生类成员标识(选读)派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构。 8.3 多重继承与派生类成员标识(选读)歧义性问题:参见图8.3,比如行政人员兼教师,在其基类教师中有一个“教职工编号”,另一基类行政人员中也有一个“教职工编号”,如果只讲教职工编号那么是哪一个基类中的呢?这两者可能是一回事,但计算机系统并不这么认为。进一步,如果“教

18、职工编号” 是由两个基类“教师”和“行政人员”共同的基类“教职工”类继承来的,只有同一个标识符,也不能用改标识符来区分。 唯一标识问题:通常采用作用域分辨符“:”:基类名:成员名; /数据成员基类名:成员名(参数表); /函数成员 class EGStudent int No在职学号 class GStudent int No研究生号 .class Student int No学生号 . class Person int No身份证号 .class Employee int No工作证号 .class Person int No身份证号 .图8.4(a)在职研究生派生类关系 定义EGStude

19、nt类对象EGStudent1,并假定派生全部为公有派生,而int No全为公有成员:EGStud1.No /在职学号EGStud1.GStudent:No /研究生号EGStud1.GStudent.Student:No /学生号 EGStud1.GStudent.Student. Person:No /身份证号EGStud1.Employee:No /工作证号EGStud1.Employee.Person:No /身份证号两个身份证号从逻辑上讲应是一回事,但是物理上是分配了不同内存空间,是两个变量,请参见图8.4(b)。Person Person StudentEmployee GStud

20、ent EGStudentPerson成员 Person成员 Student新成员 GStudent新成员 Employee新成员 EGStudent新成员 图8.4(b)在职研究生派生类存储图 建议采用有确定字面意思的标识符,它可以被编译器简单区分出来。 如果class Person的身份证号标识为int IdPerson,则写为:EGStud1.GStudent:IdPersonEGStud1.Employee:IdPerson不必标出那么多层次的类,但写EGStud1:IdPerson是错的。 作用域分辨符不能嵌套使用,如:EGStud1.GStudent:Student:No/学生号E

21、GStud1.GStudent:Student:Person:No /身份证号是错误的。 8.3 多重继承与派生类成员标识(选读)8.3 多重继承与派生类成员标识(选读)一般数据成员总是私有成员,派生类对基类的访问只能间接进行。访问身份证号,应通过class Person中的公有成员函数(接口)GetNo()和SetNo()进行:EGStud1.Employee.Person:SetNo(no);no=EGStud1.Employee.Person:GetNo();注意:【例8.2】由圆和高多重继承派生出圆锥。 因为公有派生时,在派生类中不可以直接访问基类的私有成员,但可以直接访问基类的保护成

22、员,当需要在派生类中访问基类的数据成员时,可以将它们定义为保护的,而不是私有的。 本例中类Circle为圆;类Line为高;类Cone为圆锥,由Circle和Line公有派生而来。在Cone类中,Circle和Line类的接口完全不变,可以直接调用,这就是公有派生的优点。在Cone的成员函数中可直接访问Circle和Line中的公有成员和保护成员。 【例8.2】由圆和高多重继承派生出圆锥检证主程序:圆类Circle定义高类Line定义圆锥类Cone定义虚基类的引入:在图8.4中,两个身份证号显然是不合理的。可以把class Person这个共同基类设置为虚基类,这样就仅有一个Person基类成

23、员,从不同路径继承来的同名数据成员(身份证号)在内存中就是同一个数据。 8.4 虚基类(选读)注意:virtual 关键字只对紧随其后的基类名起作用:class Student:virtual public Person.;class Employee:virtual public Person.;虚基类(virtual base class)定义:class 派生类名:virtual 访问限定符 基类类名.;class 派生类名:访问限定符 virtual 基类类名.;8.4 虚基类(选读)图8.5 采用虚基类后在职研究生类储存图StudentGStudentEGStudentPersonS

24、tudent新成员GStudent新成员PersonEmployee新成员Person成员EGStudent新成员PersonPersonEmployee这种继承称为虚拟继承虚拟继承:在Person的位置上放的是指针,两个指针都指向Person成员存储的内存。这种继承称为虚拟继承(virtual inheritance)。8.4 虚基类(选读)派生类名:派生类名(参数总表):基类名1(参数名表1),基类名2(参数名表2),基类名n(参数名表n),成员对象名1(成员对象参数名表1),成员对象名m(成员对象参数名表m),底层虚基类名1(参数名表1), 底层虚基类名r(参数名表r)/派生类新增成员的

25、初始化; /所列出的成员对象名全部为新增成员对象的名字在多层虚拟继承构造函数中,基类名不仅要列出直接基类,而且要列出底层虚基类,否则编译器认为出错。如不是虚拟继承只能列直接基类。虚拟继承的构造函数:8.4 虚基类(选读)在派生类对象的创建中:首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用。构造函数执行次序:8.4 虚基类(选读)【例8.3】在采用虚基类的多重继承中构造与析构的次序。class Objectpublic:Object()coutconstructor Objectn;Obj

26、ect()coutdeconstructor Objectn;class Bclass1public:Bclass1()coutconstructor Bclass1n;Bclass1()coutdeconstructor Bclass1n;class Bclass2public:Bclass2()coutconstructor Bclass2n;Bclass2()coutdeconstructor Bclass2n;8.4 虚基类(选读)【例8.3】在采用虚基类的多重继承中,构造与析构的次序。class Bclass3public:Bclass3()coutconstructor Bclas

27、s3n;Bclass3()coutdeconstructor Bclass3n;class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2 Object object;public: Dclass():object(),Bclass2(),Bclass3(),Bclass1() cout派生类建立!n; Dclass()cout派生类析构!n;int main() Dclass dd; coutPrintStudentInfo();pper4=pstu4;delete pper4;/用基类指针撤销派生类,动态生成的对象必须显式撤销8.

28、6.1 虚函数的定义在主函数中添加以下内容: 通过在析构函数中加显示语句发现先调Student析构函数,后调Person析构函数。 这里再次强调动态生成的对象必须显式撤销。纯虚函数: 纯虚函数(pure virtual function)是指被标明为不具体实现的虚拟成员函数。它用于这样的情况:定义一个基类时,会遇到无法定义基类中虚函数的具体实现,其实现依赖于不同的派生类。8.6.2 纯虚函数纯虚函数的定义:virtual 返回类型 函数名(参数表)=0;含有纯虚函数的基类是不能用来定义对象的。纯虚函数没有实现部分,不能产生对象,所以含有纯虚函数的类是抽象类。1 定义纯虚函数时,不能定义虚函数的

29、实现部分。即使是函数体为空也不可以,函数体为空就可以执行,只是什么也不做就返回。但根本不能调用纯虚函数。2 “=0”表明程序员将不定义该函数,函数声明是为派生类保留一个位置。“=0”本质上是将指向函数体的指针定为NULL。3 在派生类中必须有重新定义的纯虚函数的函数体,这样的派生类才能用来定义对象。8.6.2 纯虚函数定义纯虚函数的要点:【例8.8】学校对在册人员进行奖励,依据是业绩分,但是业绩分的计算方法只能对具体人员进行,如学生,教师,行政人员,工人,算法都不同,所以可以将在册人员类作为一个抽象类,业绩计算方法作为一个纯虚函数。在主函数中全部用指向基类的指针来调用8.6.2 纯虚函数业绩分

30、基类定义业绩分学生派生类定义业绩分教师派生类定义验证主函数【例8.9】用虚函数来实现辛普生法求函数的定积分。8.6.2 纯虚函数纯虚函数实现通用算法:辛普生法求定积分类在派生类中加被积函数:验证主函数8.6.3 继承与多态的应用单链表派生类(选读)【例8.10】通用单链表派生类。第一步改造【例7.4】的头文件,不采用模板类,而采用虚函数实现多态性,达到通用的目的。结点类数据域被改造为指针,而把数据放在一个抽象类中,由指针与之建立联系。数据域(指向抽象数据类的指针)由抽象类派生的数据类对象(如串对象)指针域(指向下一结点)结点类对象动态建立的数据类对象图8.9 结点构造class Object

31、/数据类为抽象类public: Object() virtual bool operator(Object &)=0; /纯虚函数,参数必须为引用或指针 virtual bool operator!=(Object &)=0; /纯虚函数,参数必须为引用或指针 virtual void Print()=0; /纯虚函数 virtual Object() ; /析构函数可为虚函数,构造函数不行8.6.3 继承与多态的应用单链表派生类(选读)结点组织,采用结点类加数据类数据类定义:本题要点:采用虚函数实现多态性,达到通用的目的。堆内存的分配与释放,关键不是创建,而是释放!说明:数据抽象类中含有三个

32、纯虚函数:输出函数和两个比较函数。当抽象类在派生时重新定义三个纯虚函数,可以进行各种类型,包括类和结构对象的比较和输出。本例介绍程序总体组成为主,链表的操作由学生自己仔细阅读。8.6.3 继承与多态的应用单链表派生类(选读)抽象类中的析构函数也是虚函数,这一点非常重要。当由结点类指向抽象基类的指针删除释放动态分配的由抽象类派生的数据类对象时,必须由数据类自定义的虚析构函数来释放该类对象数据部分占用内存。 Class Node Object* info; /数据域用指针指向数据类对象 Node* link; /指针域public: Node(); /生成头结点的构造函数 Node(); /析构函

33、数 void InsertAfter(Node* P); /在当前结点后插入一个结点 Node* RemoveAfter(); /删除当前结点的后继结点,返回该结点备用 void Linkinfo(Object* obj); /把数据对象连接到结点 friend class List; /以List为友元类,List可直接访问Node的私有函数,;8.6.3 继承与多态的应用单链表派生类(选读)结点类定义:class List Node *head,*tail; /链表头指针和尾指针public: List(); /构造函数,生成头结点(空链表) List(); /析构函数 void Make

34、Empty(); /清空链表,只余表头结点 Node* Find(Object & obj); /搜索数据域与定值相同的结点,返回该结点的地址 int Length(); /计算单链表长度 void PrintList(); /打印链表的数据域 void InsertFront(Node* p); /可用来向前生成链表 void InsertRear(Node* p); /可用来向后生成链表 void InsertOrder(Node* p); /按升序生成链表 Node* CreatNode(); /创建一个结点(孤立结点) Node* DeleteNode(Node* p); ; /删除指

35、定结点8.6.3 继承与多态的应用单链表派生类(选读)链表类定义:第二步,取代模板定义泛型类型为具体类型(包括类)的步骤是由抽象类派生数据类。数据类的数据采用字符类串string,动态分配和释放内存都在string类中完成。为了完成数据类的比较和输出,超载了比较运算符和输出函数(虚函数)。数据类的比较实际是字符串string的比较。 8.6.3 继承与多态的应用单链表派生类(选读)class StringObject:public Object string sptr;public: StringObject() sptr=; StringObject(string s)sptr=s; Str

36、ingObject(); /析构函数 bool operator(Object &); /大于函数 bool operator!=(Object &); /不等于函数 void Print(); /打印函数;验证主函数运行结果分析与比较: 在该程序中,特别要仔细揣摩堆内存的分配与释放。删除一个结点时系统自动调用结点类析构函数释放结点占用的动态内存,而结点类析构函数自动调用数据域类虚析构函数,数据域类析构函数自动调用string类的析构函数释放所占用的动态内存。一环套一环,一步都不能错。这是使用动态内存分配的关键。即关键不是创建,而是释放! 运行时的多态性需要维护一个动态指针表才能正确指向各相关

37、类中的同名虚函数。所以多态与模板比较,模板的效率更高,标准模板库中用容器来泛型化数据结构中的许多算法。对数据结构的使用当然借助模板库。多态不适用于性能要求很高的实时应用程序,但继承与多态可用与其它更多方面,每一种技术都有可以充分发挥自己能力的地方。8.6.3 继承与多态的应用单链表派生类(选读)动态绑定(dynamic binding)亦称滞后绑定(late binding),对应于静态绑定(static binding)。如果使用对象名和点成员选择运算符“.”引用特定的一个对象来调用虚函数,则被调用的虚函数是在编译时确定的(称为静态绑定) 如果使用基类指针或引用指明派生类对象并使用该指针调用

38、虚函数(成员选择符用箭头号“-”),则程序动态地(运行时)选择该派生类的虚函数,称为动态绑定。8.6.4 动态绑定(选读)绑定是指计算机程序自身彼此关联的过程,是把一个标识符名和一个存储地址联系在一起的过程,也就是把一条消息和一个对象的操作相结合的过程 。图8.9 虚函数调用的控制流程“dog”StringObject动态无名对象StringObject动态无名对象“cat”指向Object类指针指向结点类指针指向Object类指针指向结点类指针指向Object类指针指向结点类指针StringObject动态无名对象“cock” 析构函数指针比较函数指针输出函数指针StringObject虚函

39、数表抽象类Object虚函数表析构函数指针比较函数指针输出函数指针ComplexObject虚函数 析构函数指针 比较函数指针 输出函数指针 默认析构函数释放动态串析构函数串比较函数打印串函数默认析构函数复数模大小比较函数打印复数函数8.6.4 动态绑定(选读) C+编译器编译含有一个或几个虚函数的类及 其派生类时,对该类建立虚函数表(Virtual function table,vtable)。 虚函数表使执行程序正确选择每次执行时应使用的虚函数。 多态是由复杂的数据结构实现的,参见图8.10。图8.10是以【例8.10】为基础的,不过增加了一个由抽象类Object派生的复数数据类Compl

40、exObject。图中列出了基类和各派生类的虚函数表,这些表是由指向函数的指针组成的。 8.6.4 动态绑定(选读) 还有第二层指针,在实例化带虚函数的类(创建对象)时,编译器在对象前加上一个指向该类的虚函数表的指针。 第三层指针是链表结点类对象中指向抽象基类Object的指针(这也可以是引用,但本例是指针)。 虚函数的调用是这样进行的,考虑虚函数Compare(),则看含“cat”的结点。由该结点的info指针找到含“cat”的无名对象,再由对象前的指针找到StringObject虚函数表,移动4个字节(一个指针占4个字节)找到比较函数指针,进入串比较函数。UML类图中的依赖关系依赖(dep

41、endency)依赖表示两个元素之间存在一种关系,其中一个元素(提供者)的变化将影响另一个元素(客户),或向它(客户)提供所需信息。但两者反过来是不成立的。这是将数种不同的建模关系组织到一起的简便方法。在UML的基本模型中的依赖关系在C+编程中常见的有:绑定(bind):为模板参数指定值,以生成一个新的模型元素。等效C+模板中的实例化。属绑定依赖。访问(access):允许某个包访问另一个包的内容。以下属许可依赖。友元(friend):允许某元素访问另一个元素,而不管被访问者是否可见。UML类图中的依赖关系依赖(dependency)调用(call):声明某个类调用其他类的操作方法。以下属使用

42、依赖。参数(parameter):一个操作和它的参数之间的关系。实例化(instantiate):这里的实例化不同于C+模板,而是泛指从概念到实体,即创建实例。如由类创建对象、由用例创建用例实例。创建实例的机制是运行时环境的职责。发送(send):信号发送者与接受者之间的关系。另外有抽象依赖,包括跟踪(trace)、精化(refine)、实现(realize)、导出(derive)。UML类图中的依赖关系依赖通常用一个从客户指向提供者的虚箭头表示。例:下图中类time12是12小时计时,精确到分;而time24是24小时计时,精确到秒。可以将24小时制转换到12小时制,反之不行。类time12

43、是客户,类time24是提供者。UML类图中的依赖关系*绑定: 在计算机编程中,绑定是在某个时间范围和特定的位置内为两个或更多编程对象或值对象创建联系的过程,内涵十分广泛。编译程序时,绑定意味着用一个真实值替换程序中的变量值,或用来保证另一些程序和被编译的程序一起被加载到存储器中。在本C+教材中提到静态绑定和动态绑定是一个具体的实例,也译作静态联编和动态联编;本教材也提到类模板和函数模板分两步进行编译,其中模板实例化也是绑定的一个实例。但是在同一个地方总是要用不同的名称来区分具体的事物,这就造成了C+和UML的术语选用的差异。绑定这个术语也用于网络通信中,任何两个网络终端、实体、过程或逻辑单元

44、之间建立起一个明确的连接都可以称为绑定。 完第八章 继承与派生谢谢!【例8.1】由在册人员类公有派生学生类Person:Person(string id, string name,Tsex sex,int birthday, string homeadd)IdPerson=id;Name=name;Sex=sex;Birthday=birthday;HomeAddress=homeadd; /作为一个管理程序,这个构造函数并无必要,因为数据总是另外输入的。仅为说明语法存在。分析构造函数:【例8.1】由在册人员类公有派生学生类Person:Person()IdPerson=#;Name=#;Se

45、x=mid;Birthday=0;HomeAddress=#;分析默认的构造函数:分析析构函数:Person:Person() /string内部动态数组的释放,由string自带的析构函数完成【例8.1】由在册人员类公有派生学生类void Person:SetName(string name)Name=name; /拷入新姓名修改名字:void Person:SetHomeAdd(string homeadd)HomeAddress=homeadd;修改住址:【例8.1】由在册人员类公有派生学生类void Person:PrintPersonInfo()int i;cout身份证号:IdPe

46、rsonn姓名: Namen性别:;if(Sex=man)cout男n;else if(Sex=woman)cout女n; else cout n;cout出生年月日:;i=Birthday;couti/10000年;i=i%10000;couti/100月i%100日n 家庭住址:HomeAddressn;输出个人信息:【例8.1】由在册人员类公有派生学生类Student:Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud) :Person(id,name,sex,birthd

47、ay,homeadd) /注意Person参数名表不用类型int i;NoStudent=nostud;for(i=0;i30;i+) /课程与成绩清空csi.coursename=#;csi.grade=0;派生类构造函数:【例8.1】由在册人员类公有派生学生类Student:Student()/基类默认的无参数构造函数不必显式给出int i; NoStudent=“#;for(i=0;i30;i+) /课程与成绩清空,将来由键盘输入 csi.coursename=“#;csi.grade=0;Student:Student() /基类析构函数以及成员对象析构函数自动调用默认派生类构造函数:

48、派生类析构函数:【例8.1】由在册人员类公有派生学生类int Student:SetCourse(string coursename,int grade) bool b=false; /标识新输入的课程,还是更新成绩 int i; for(i=0;i30;i+) if(csi.coursename=#) /判表是否进入未使用部分(如有对应删除,应按顺序表方式)csi.coursename=coursename;csi.grade=grade;b=false;break; else if(csi.coursename=coursename)/是否已有该课程记录 csi.grade=grade;b

49、=true;break; if(i=30) return 0; /成绩表满返回0 if(b) return 1; /修改成绩返回1 else return 2; /登记成绩返回2学生类课程设置函数:【例8.1】由在册人员类公有派生学生类int Student:GetCourse(string coursename) int i; for(i=0;i30;i+) if(csi.coursename=coursename) return csi.grade; return -1; /找到返回成绩,未找到返回-1查找学生课程成绩函数:【例8.1】由在册人员类公有派生学生类void Student:P

50、rintStudentInfo()int i;cout学号:NoStudentn;PrintPersonInfo();for(i=0;i30;i+) /打印各科成绩if(csi.coursename!=#) coutcsi.coursename tcsi.graden;else break;cout-完- endl;打印学生情况函数:例8.1验证用主函数:int main(void) char temp30; int i,k; Person per1(320102820818161,沈俊, man,19820818,南京四牌楼2号); Person per2; per2.SetName(朱明)

51、; per2.SetSex(woman); per2.SetBirth(19780528); per2.SetId(320102780528162); per2.SetHomeAdd(南京市成贤街9号); per1.PrintPersonInfo(); per2.PrintPersonInfo(); Student stu1(320102811226161,朱海鹏, man,19811226,南京市黄浦路1号,06000123); cout请输入各科成绩:temp; /输入格式:物理 80 if(!strcmp(temp,end) break; cink; i=stu1.SetCourse(t

52、emp,k); if(i=0) cout成绩列表已满!n; else if(i=1) cout修改成绩n; else cout登记成绩n; stu1.PrintStudentInfo(); while(1) cout查询成绩n请输入科目:temp; if(!strcmp(temp,end) break; k=stu1.GetCourse(temp); if(k=-1) cout未查到n; else coutkn; return 0;【例8.2】由圆和高多重继承派生出圆锥class Circleprotected: float x,y,r; /(x,y)为圆心,r为半径public: Circl

53、e(float a=0,float b=0,float R=0)x=a;y=b;r=R; void Setcoordinate(float a,float b)x=a;y=b; /设置圆心坐标 void Getcoordinate(float &a,float &b)a=x;b=y; void SetR(float R)r=R;/设置半径 float GetR()return r;/取圆半径 float GetAreaCircle()return float(r*r*3.14159); /取圆面积 float GetCircumference()return float(2*r*3.14159

54、); /取圆周长;高类Line:【例8.2】由圆和高多重继承派生出圆锥class Lineprotected: float High;public: Line(float a=0)High=a; void SetHigh(float a)High=a; float GetHigh()return High;class Cone:public Circle,public Linepublic: Cone(float a,float b,float R,float d):Circle(a,b,R),Line(d) float GetCV()return float(GetAreaCircle()*

55、High/3); /取得圆锥体积 float GetCA() /取得圆锥表面积 return float(GetAreaCircle()+r*3.14159*sqrt(r*r+High*Hgih); /共有派生类中能直接访问直接基类的保护成员【例8.2】由圆和高多重继承派生出圆锥派生类圆锥:在VC+平台上运行例8.2int main()Cone c1(5,8,3,4);float a,b;cout圆锥体积:c1.GetCV()n;cout圆锥表面积:c1.GetCA()n;cout圆锥底面积:c1.GetAreaCircle()n;cout圆锥底周长:c1.GetCircumference()

56、n;cout圆锥底半径:c1.GetR()n;c1.Getcoordinate(a,b);cout圆锥底圆心坐标:(a,b)n;cout圆锥高:c1.GetHigh()n;return 0;【例8.2】由圆和高多重继承派生出圆锥检证主程序:class Student:public virtual Personstring NoStudent; /学号/30门课程与成绩略public:Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud);Student(); Student()co

57、ut析构Studentendl;void PrintStudentInfo(); ;Student:Student(string id, string name,Tsex sex, int birthday, string homeadd, string nostud) :Person(id,name,sex,birthday,homeadd) /注意Person参数名表不用类型cout构造Studentendl;NoStudent=nostud;例8.4 虚基类与在职研究生以虚基类定义公有派生的学生类:Student:Student() /基类默认的无参数构造函数不必显式给出cout构造St

58、udentendl;void Student:PrintStudentInfo()cout学号:NoStudentn;PrintPersonInfo();例8.4 虚基类与在职研究生以虚基类定义公有派生的学生类:class GStudent:public Student /以虚基类定义公有派生的研究生类 string NoGStudent; /研究生号,其他略public: GStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud,string nogstudent); /注意派生类

59、构造函数声明方式 GStudent(); GStudent()cout析构GStudentendl; void PrintGStudentInfo();GStudent:GStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud, string nogstud): Student(id,name,sex,birthday,homeadd,nostud), Person(id,name,sex,birthday,homeadd) /因Person是虚基类,尽管不是直接基类, Person

60、必须出现。 /不定义对象可不出现,为通用应出现。如不是虚基类,出现是错误的 cout构造GStudentendl; NoGStudent=nogstud;例8.4 虚基类与在职研究生以虚基类定义公有派生的研究生类:GStudent:GStudent() /基类默认的无参数构造函数不必显式给出cout构造GStudentendl;void GStudent:PrintGStudentInfo()cout研究生号:NoGStudentn;PrintStudentInfo();例8.4 虚基类与在职研究生以虚基类定义公有派生的研究生类:例8.4 虚基类与在职研究生class Employee:pub

温馨提示

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

评论

0/150

提交评论