已阅读5页,还剩97页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
继承(inheritance): 该机制是面向对象程序设计使代码可以复用的最重要的手 段,它允许程序员在保持原有类特性的基础上进行扩展, 增加功能。这样产生新的类,称派生类。继承呈现了面向 对象程序设计的层次结构。体现了由简单到复杂的认识过 程。 第八章 继承与多态 多态性(polymorphism): 多态性是考虑在不同层次的类中,以及在同一类中,同名 的成员函数之间的关系问题。函数的重载,运算符的重载 ,属于编译时的多态性。以虚函数为基础的运行时的多态 性是面向对象程序设计的标志性特征。 体现了类推和比喻 的思想方法。 第八章 继承与多态 8.1 继承与派生的概念 8.4 虚基类 (选读) 8.3 多重继承与派生类成员标识 8. 6 多态性与虚函数 8.5 派生类应用讨论 8.2 派生类的构造函数与析构函数 8.1 继承与派生的概念 层次概念是计算机的重要概念。通过继承(inheritance) 的机制可对类(class)分层,提供类型/子类型的关系。 C+通过类派生(class derivation)的机制来支持继承。 被继承的类称为基类(base class)或超类(superclass), 新的类为派生类(derived class)或子类(subclass)。 基 类和派生类的集合称作类继承层次结构(hierarchy)。 如果基类和派生类共享相同的公有接口,则派生类被称作 基类的子类型(subtype)。 层次概念: 派生反映了事物之间的联系,事物的共性与个性之间的关系 。 派生与独立设计若干相关的类,前者工作量少,重复的部 分可以从基类继承来,不需要单独编程。 8.1 继承与派生的概念 8.1.1 类的派生与继承 8. 1.2 公有派生与私有派生 派生类的定义: class 派生类名:访问限定符 基类名1,访问限定符 基类名2,访问限定符 基类名n private: 成员表1; /派生类增加或替代的私有成员 public: 成员表2; /派生类增加或替代的公有成员 protected: 成员表3; /派生类增加或替代的保护成员 ;/分号不可少 其中基类1,基类2,是已声明的类。 在派生类定义的类体中 给出的成员称为派生类成员,它们是新增加成员,它们给派生类 添加了不同于基类的新的属性和功能。派生类成员也包括取代基 类成员的更新成员。 8.1.1 类的派生与继承 基类1基类2基类n 派生类1派生类2 基类 派生类1派生类2 (a)多重继承 (b)单继承 图8.1 多重继承与单继承 一个基类 可以直接 派生出多 个派生类 派生类可 以由多个 基类共同 派生出来 ,称多重 继承。 8.1.1 类的派生与继承 多重继承:如果一个派生类可以同时有多个基类,称 为多重继承(multiple-inheritance),这时的派生类同 时得到了多个已有类的特征。 单继承:派生类只有一个直接基类的情况称为单继承( single-inheritance)。 8.1.1 类的派生与继承 在派生过程中,派生出来的新类同样可以 作为基类再继续派生出更新的类,依此类推形 成一个层次结构。直接参与派生出某类称为直 接基类,而基类的基类,以及更深层的基类称 为间接基类。 类族: 同时一个基类可以直接派生出多个派生类 。这样形成了一个相互关联的类族。如MFC就 是这样的族类,它由一个CObject类派生出200 个MFC类中的绝大多数。 多层次继承: 编制 派生 类时 可分 四步 吸收基类的成员 改造基类成员 发展新成员 重写构造函数与析构函数 8.1.1 类的派生与继承 不论是数据成员,还是函数成员, 除构造函数与析构函数外全盘接收 声明一个和某基类成员同名的新成员,派 生类中的新成员就屏蔽了基类同名成员 称为同名覆盖(override) 派生类新成员必须与基类成员不同名,它 的加入保证派生类在功能上有所发展。 派生编程步骤: 8.1.1 类的派生与继承 第二步中,新成员如是成员函数,参数表也必须一样,否则 是重载。 第三步中,独有的新成员才是继承与派生的核心特征。 第四步是重写构造函数与析构函数,派生类不继承这两种函 数。不管原来的函数是否可用一律重写可免出错。 访问控制: 亦称为继承方式,是对基类成员进一步的限制。访问控制也 是三种: 公有(public)方式,亦称公有继承 保护(protected)方式,亦称保护继承 私有(private)方式, 亦称私有继承。 8.1.2 公有派生与私有派生 不可直接访问 不可直接访 问 private 不可直接访问 private protected 不可直接访问 private public 私有派生 不可直接访问 不可直接访 问 private 不可直接访问 protected protected 可直接访问 public public 公有派生 在派生类对象 外访问派生类 对象的基类成 员 在派生类中 对基类成员 的访问限定 基类中的 访问限定 派生方式 访问限定符两方面含义:派生类成员(新增成员)函数 对基类(继承来的)成员的访问(调用和操作),和从 派生类对象之外对派生类对象中的基类成员的访问。 公有派生是绝对主流。 派生类构造函数的定义: 派生类名:派生类名(参数总表):基类名1(参数名表1) ,基类名2(参数名表2),基类名n(参数名表n ),成员对象名1(成员对象参数名表1),成 员对象名m(成员对象参数名表m) /派生类新增成员的初始化; /所列出的成员对象名全部为新增成员对象的名字 注意: 在构造函数的声明中,冒号及冒号以后部分必须略去。 所谓不能继承并不是不能利用,而是把基类的构造函数作 为新的构造函数的一部分,或者讲调用基类的构造函数。基 类名仅指直接基类,写了底层基类,编译器认为出错。 冒号后的基类名,成员对象名的次序可以随意,这里的次 序与调用次序无关。 8.2 派生类的构造函数与析构函数 派生类构造函数各部分执行次序: 1.调用基类构造函数,按它们在派生类定义 的先后顺序,顺序调用。 2.调用成员对象的构造函数,按它们在类定 义中声明的先后顺序,顺序调用。 3.派生类的构造函数体中的操作。 8.2 派生类的构造函数与析构函数 注意: 在派生类构造函数中,只要基类不是使用无参的默认 构造函数都要显式给出基类名和参数表。 如果基类没有定义构造函数,则派生类也可以不定义 ,全部采用系统给定的默认构造函数。 如果基类定义了带有形参表的构造函数时,派生类就 应当定义构造函数。 8.2 派生类的构造函数与析构函数 析构函数: 析构函数的功能是作善后工作。 只要在函数体内把派生类新增一般成员处理好 就可以了,而对新增的成员对象和基类的善后工作 ,系统会自己调用成员对象和基类的析构函数来完 成。 析构函数各部分执行次序与构造函数相反,首 先对派生类新增一般成员析构,然后对新增对象成 员析构,最后对基类成员析构。 【例8.1】由在册人员类公有派生学生类 【例8.1】由在册人员类公有派生学生类。我 们希望基类和派生类共享相同的公有接口,只 能采用公有派生来实现。 基类: class Person string IdPerson;/身份证号,18位数字 string Name;/姓名 Tsex Sex; /性别enum Tsexmid,man,woman; int Birthday;/生日,格式1986年8月18日写作19860818 string 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; /提取性别 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】由在册人员类公有派生学生类 派生的学生类: 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 ); /查找成绩 void PrintStudentInfo(); /打印学生情况 ; struct course string coursename; int grade; 验证主函数 8.2 派生类的构造函数与析构函数 注意: 本例中标准C+字符串string是作为成员对象使用的( 聚合),动态内存分配的构造和析构被封装起来,使用十分 简单。如使用动态生成的C风格字符串,要考虑深复制,那 要复杂得多。 提倡完善的类对象封装,不仅封装数据和对数据的操作 ,而且封装资源的动态分配与释放,形成一个完备的子系统 。在一个有层次结构的类体系中资源的动态分配与释放应封 装在成员对象中,如同使用标准的string字符串类那样。 聚合是一种完善的封装。采用成员对象将大大简化层次 结构的类体系中资源的动态分配与释放的处理方法,不再出 现难度极大的多层次的深复制。 8.3 多重继承与派生类成员标识(选读) 由多个基类共同派生出新的派生类,这样的继承结构 被称为多重继承或多继承(multiple-inheritance) 椅子床 沙发(单继承)躺椅(多重继承) 两用沙发(多重继承) 图8.2 椅子,床到两用沙发 多重继承实例: 在册人员 学生(单继承) 教职工(单继承)兼职教师(单继承) 教师(单继承) 行政人员(单继承)工人(单继承) 研究生(单继承) 行政人员兼教师 (多重继承) 在职研究生 (多重继承) 研究生助教 (多重继承) 图8.3 大学在册人员继承关系 8.3 多重继承与派生类成员标识(选读) 派生出来 的新类同 样可以作 为基类再 继续派生 出更新的 类,依此 类推形成 一个层次 结构。 8.3 多重继承与派生类成员标识(选读) 歧义性问题: 参见图8.3,比如行政人员兼教师,在其基类教师中有一个“ 教职工编号”,另一基类行政人员中也有一个“教职工编号” ,如果只讲教职工编号那么是哪一个基类中的呢?这两者可 能是一回事,但计算机系统并不这么认为。 进一步,如果“教职工编号” 是由两个基类“教师”和“行政人 员”共同的基类“教职工”类继承来的,只有同一个标识符, 也不能用改标识符来区分。 唯一标识问题: 通常采用作用域分辨符“:”: 基类名:成员名; /数据成员 基类名:成员名(参数表); /函数成员 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)在职研究生派生类关系 定义EGStudent类对象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 Student Employee GStudent EGStudent Person成员 Person成员 Student新成员 GStudent新成员 Employee新成员 EGStudent新成员 图8.4(b)在职研究 生派生类存储图 建议采用有确定字面意思的标识符,它可以被编译器简单区分出 来。 如果class Person的身份证号标识为int IdPerson,则写为: EGStud1.GStudent:IdPerson EGStud1.Employee:IdPerson 不必标出那么多层次的类,但写EGStud1.IdPerson是错的。 作用域分辨符不能嵌套使用,如: EGStud1.GStudent:Student:No/学生号 EGStud1.GStudent:Student:Person:No /身份证号 是错误的。 8.3 多重继承与派生类成员标识(选读) 8.3 多重继承与派生类成员标识(选读) 一般数据成员总是私有成员,派生类对基类 的访问只能间接进行。访问身份证号,应通 过class Person中的公有成员函数(接口) GetNo()和SetNo()进行: EGStud1.Employee.Person:SetNo(int no); no=EGStud1.Employee.Person:GetNo(); 注意: 【例8.2】由圆和高多重继承派生出圆锥。 因为公有派生时,在派生类中不可以直接访问基类的私有成 员,但可以直接访问基类的保护成员,当需要在派生类中访问基 类的数据成员时,可以将它们定义为保护的,而不是私有的。 本例中类Circle为圆;类Line为高;类Cone为圆锥,由 Circle和Line公有派生而来。在Cone类中,Circle和Line类的接 口完全不变,可以直接调用,这就是公有派生的优点。在Cone 的成员函数中可直接访问Circle和Line中的公有成员和保护成员 。 【例8.2】由圆和高多重继承派生出圆锥 检证主程序: 圆类Circle定义 高类Line定义 圆锥类Cone定义 虚基类的引入: 在图8.4中,两个身份证号显然是不合理的。可以把 class Person这个共同基类设置为虚基类,这样就仅有 一个Person基类成员,从不同路径继承来的同名数据成 员(身份证号)在内存中就是同一个数据。 8.4 虚基类(选读) 注意: virtual 关键字只对紧随其后的基类名起作用: class Student:virtual public Person.; class Employee:virtual public Person.; 虚基类(virtual base class)定义: class 派生类名:virtual 访问限定符 基类类名.; class 派生类名:访问限定符 virtual 基类类名.; 8.4 虚基类(选读) 图8.5 采用虚基类后在职研究生类储存图 Student GStudent EGStudent Person Student新成员 GStudent新成员 Person Employee新成员 Person成员 EGStudent新成员 Person Person Employee 这种继承称 为虚拟继承 虚拟继承: 在Person的位置上放的是指针,两个指针都指向 Person成员存储的内存。这种继承称为虚拟继承 (virtual inheritance)。 8.4 虚基类(选读) 派生类名:派生类名(参数总表):基类名1(参数名表1) ,基类名2(参数名表2),基类名n(参数名表n), 成员对象名1(成员对象参数名表1),成员对象名m( 成员对象参数名表m),底层虚基类名1(参数名表1) , 底层虚基类名r(参数名表r) /派生类新增成员的初始化 ; /所列出的成员对象名全部为新增成员对象的名字 在多层虚拟继承构造函数中,基类名不仅要列出 直接基类,而且要列出底层虚基类,否则编译器 认为出错。 虚拟继承的构造函数: 8.4 虚基类(选读) 在派生类对象的创建中: 首先是虚基类的构造函数并按它们声明的顺序构造。 第二批是非虚基类的构造函数按它们声明的顺序调用。 第三批是成员对象的构造函数。 最后是派生类自己的构造函数被调用。 构造函数执行次序: 8.4 虚基类(选读) 【例8.3】在采用虚基类的多重继承中 ,构造与析构的次序。 class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2 Object object; public: Dclass():object(),Bclass2(),Bclass3(),Bclass1() coutPrintStudentInfo(); per4=stu4; delete per4; /用基类指针撤销派生类,动态生成的对象必须显式撤销 8.6.1 虚函数的定义 在主函数中添加以下内容: 通过在析构函数中加显示语句发现先调Student析构函 数,后调Person析构函数。 这里再次强调动态生成的对象必须显式撤销。 纯虚函数: 纯虚函数(pure virtual function)是指 被标明为不具体实现的虚拟成员函数。它用 于这样的情况:定义一个基类时,会遇到无 法定义基类中虚函数的具体实现,其实现依 赖于不同的派生类。 8.6.2 纯虚函数 纯虚函数的定义: virtual 返回类型 函数名(参数表)=0; 含有纯虚函数的基类是不能用来定义对象的。纯虚 函数没有实现部分,不能产生对象,所以含有纯虚 函数的类是抽象类。 1 定义纯虚函数时,不能定义虚函数的实现部分。 即使是函数体为空也不可以,函数体为空就可以执 行,只是什么也不做就返回。而纯虚函数不能调用 。 2 “=0”表明程序员将不定义该函数,函数声明是 为派生类保留一个位置。“=0”本质上是将指向函数 体的指针定为NULL。 3 在派生类中必须有重新定义的纯虚函数的函数 体,这样的派生类才能用来定义对象。 8.6.2 纯虚函数 定义纯虚函数的要点: 【例8.8】学校对在册人员进行奖励,依据是业 绩分,但是业绩分的计算方法只能对具体人员进 行,如学生,教师,行政人员,工人,算法都不 同,所以可以将在册人员类作为一个抽象类,业 绩计算方法作为一个纯虚函数。 在主函数中全部用指向基类的指针来调用 8.6.2 纯虚函数 业绩分基类定义 业绩分学生派生类定义 业绩分教师派生类定义 验证主函数 【例8.9】用虚函数来实现辛普生法求函数的定积分。 8.6.2 纯虚函数 纯虚函数实现通用算法: 辛普生法求定积分类 在派生类中加被积函数: 验证主函数 8.6.3 继承与多态的应用单链表派生类(选读) 【例8.10】通用单链表派生类。第一步改造【例7.4】 的头头文件,不采用模板类类,而采用虚函数实现实现 多态态性,达 到通用的目的。结结点类类数据域被改造为为指针针,而把数据放 在一个抽象类类中,由指针针与之建立联联系。 数据域 (指向抽象 数据类的指 针) 由抽象类派 生的数据类 对象(如串 对象) 指针域(指 向下一结 点) 结点类对象 动态建立的 数据类对象 图8.9 结点构造 class Object /数据类为抽象类 public: Object() virtual bool operator(Object /纯虚函数,参数必须为引用或指针 virtual bool operator!=(Object /纯虚函数,参数必须为引用或指针 virtual void Print()=0; /纯虚函数 virtual Object() ; /析构函数可为虚函数,构造函数不行 8.6.3 继承与多态的应用单链表派生类(选读) 结点组织,采用结点类加数据类 数据类定义: 本题要点: 采用虚函数实现多态性,达到通用的目的。 堆内存的分配与释放,关键不是创建,而是释放! 说明: 数据抽象类中含有三个纯虚函数:输出函数和两个比 较函数。当抽象类在派生时重新定义三个纯虚函数, 可以进行各种类型,包括类和结构对象的比较和输出 。 本例介绍程序总体组成为主,链 表的操作由学生自己仔细阅读。 8.6.3 继承与多态的应用单链表派生类(选读) 抽象类中的析构函数也是虚函数,这一点非常重要 ,当抽象类派生的数据类的数据部分是动态产生的 ,而由结点类删除释放数据类对象时,必须由数据 类的析构函数来释放该类对象数据部分占用的动态 分配的内存。这时必须重新定义析构函数。 Class Node Object* info; /数据域用指针指向数据类对象 Node* link; /指针域 public: Node(); /生成头结点的构造函数 Node(); /析构函数 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 MakeEmpty(); /清空链表,只余表头结点 Node* Find(Object /搜索数据域与定值相同的结点,返回该结点的地址 int Length(); /计算单链表长度 void PrintList(); /打印链表的数据域 void InsertFront(Node* p); /可用来向前生成链表 void InsertRear(Node* p); /可用来向后生成链表 void InsertOrder(Node* p); /按升序生成链表 Node* CreatNode(); /创建一个结点(孤立结点) Node* DeleteNode(Node* p); ; /删除指定结点 8.6.3 继承与多态的应用单链表派生类(选读) 链表类定义: 第二步,取代模板定义泛型类型为具体类型(包括类)的 步骤是由抽象类派生数据类。数据类的数据采用字符类串 string,动态分配和释放内存都在string类中完成。为了完 成数据类的比较和输出,超载了比较运算符和输出函数( 虚函数)。数据类的比较实际是字符串string的比较。 8.6.3 继承与多态的应用单链表派生类(选读) class StringObject:public Object string sptr; public: StringObject() sptr=“; StringObject(string s)sptr=s; StringObject(); /析构函数 bool operator(Object /大于函数 bool operator!=(Object /不等于函数 void Print(); /打印函数 ; 验证主函数 运行结果 分析与比较: 在该程序中,特别要仔细揣摩堆内存的分配与释放。删 除一个结点时系统自动调用结点类析构函数释放结点占用的 动态内存,而结点类析构函数自动调用数据域类虚析构函数 ,数据域类析构函数自动调用string类的析构函数释放所占用 的动态内存。一环套一环,一步都不能错。这是使用动态内 存分配的关键。即关键不是创建,而是释放! 运行时的多态性需要维护一个动态指针表才能正确指向 各相关类中的同名虚函数。所以多态与模板比较,模板的效 率更高,标准模板库中用容器来泛型化数据结构中的许多算 法。对数据结构的使用当然借助模板库。多态不适用于性能 要求很高的实时应用程序,但继承与多态可用与其它更多方 面,每一种技术都有可以充分发挥自己能力的地方。 8.6.3 继承与多态的应用单链表派生类(选读) 动态绑定(dynamic binding)亦称滞后绑定( late binding),对应于静态绑定(static binding )。 如果使用对象名和点成员选择运算符“.”引用 特定的一个对象来调用虚函数,则被调用的虚 函数是在编译时确定的(称为静态绑定) 如果使用基类指针或引用指明派生类对象并使用该指针 调用虚函数(成员选择符用箭头号“-”),则程序动态 地(运行时)选择该派生类的虚函数,称为动态绑定。 8.6.4 动态绑定(选读) 绑定是指计算机程序自身彼此关联的过程,是把一个标识符 名和一个存储地址联系在一起的过程,也就是把一条消息和 一个对象的操作相结合的过程 。 图8.9 虚函数调用的控制流程 “dog” StringObject 动态无名对象 StringObject 动态无名对象 “cat” 指向Object类指针 指向结点类指针 指向Object类指针 指向结点类指针 指向Object类指针 指向结点类指针 StringObject 动态无名对象 “cock” 析构函数指针 0比较函数指针 0输出函数指针 StringObject虚函数表 抽象类Object虚函数表 析构函数指针 比较函数指针 输出函数指针 ComplexObject虚函数 析构函数指针 比较函数指针 输出函数指针 默认析 构函数 释放动态串 析构函数 串比较函数 打印串函数 默认析构 函数 复数模大小 比较函数 打印复数函数 8.6.4 动态绑定(选读) C+编译器编译含有一个或几个虚函数的类及 其派生类时,对该类建立虚函数表(Virtual function table,vtable)。 虚函数表使执行程序正确选择每次 执行时应使用的虚函数。 多态是由复杂的数据结构实现的,参见图8.10。图 8.10是以【例8.10】为基础的,不过增加了一个由抽象 类Object派生的复数数据类ComplexObject。图中列 出了基类和各派生类的虚函数表,这些表是由指向函 数的指针组成的。 8.6.4 动态绑定(选读) 还有第二层指针,在实例化带虚函数的类(创 建对象)时,编译器在对象前加上一个指向该类的 虚函数表的指针。 第三层指针是链表结点类对象中指向抽象基类 Object的指针(这也可以是引用,但本例是指针) 。 虚函数的调用是这样进行的,考虑虚函数 Compare(),则看含“cat”的结点。由该结点的info 指针找到含“cat”的无名对象,再由对象前的指针找 到StringObject虚函数表,移动4个字节(一个指针 占4个字节)找到比较函数指针,进入串比较函数。 完 第八章 继承与派生 谢谢! 【例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=“#“;Sex=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; couttemp; /输入格式:物理 80 if(!strcmp(temp,“end“) break; cink; i=stu1.SetCourse(temp,k); if(i=0)couttemp; if(!strcmp(temp,“end“) break; k=stu1.GetCourse(temp); if(k=-1)coutPerson:operator=(Std); /注意标准格式 NoStudent=Std.NoStudent; for(int i=0;itemp; /输入格式:物理 80 if(temp=“end“) break; cink; i=stu1.SetCourse(temp,k); if(i=0)coutCalculate(); coutPrint(); ps= ps-Calculate(); coutPrint(); return 0 例8.6 虚函数计算学分 结果为: 本科生:物理 80学时 5学分 研究生:物理 80学时 4学分 本科生:数学 160学时 10学分 研究生:数学 160学时 8学分 第一行学分是由Student类的 成员函数Calculate()计算。 第二行学分是由 GradeStudent重新定义的 Calculate()计算,它屏蔽 了基类的同名函数。 第三行用的是指向Student类 的对象s的指针,当然用的是 Student类的Calculate()。 指针类型是指向基类的指针,但这里指 针指向了派生类GradeStudent的对象g ,按赋值兼容规则是准许的,但只能用 基类的成员,可实际上用了派生中新定 义的Calculate()。这就是虚函数体现的 多态性,如果不是虚函数,第四行输出 是10学分。 【例8.7】计算学分。派生类定义不再重复。 void Calfun(Student ps.Calculate(); ps.Print(); int main() Student s; GradeStudent g; coutCalMark(); pp-Print(); pp= pp-CalMark(); pp-Print(); pp= pp-CalMark(); pp-Print(); return 0; 例8.8 业绩分的计算 class Simpson;/Intevalue积分值,a积分下限,b积分上限 double Intevalue,a,b public: virtual double fun(double x)=0; /被积函数声明为纯虚函数 Simpson(double ra=0,double rb=0)a=ra;b=rb;Intevalue=0; void Integrate() double dx; int i; dx=(b-a)/2000; Intevalue=fun(a)+fun(b); for(i=1;iIntegrate(); /动态 B b1(0.0,1.0); b1.Integrate(); /静态 s-Print(); b1.Print(); return 0; 在VC+平台上运行例8.9。 Node:Node() info=NULL; link=NULL; Node:Node() coutlink!=NULL) tempP=head-link; head-link=tempP-link; /把头结点后的第一个节点从链中脱离 delete tempP; /释放该结点,先自动调用结点类的析构函数, /再自动调用数据域类的/析构函数, /不可在前面加 delete tempP-info; 以释放数据域类 tail=head; /表头指针与表尾指针均指向表头结点,表示空链 【例8.10】通用单链表派生类 Node*
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 红色义卖活动方案
- 看望产妇活动方案
- 益气养血活动方案
- 盐城就业活动方案
- 线上大课间体育活动方案
- 索菲亚营销活动方案
- 箱包新店福利活动方案
- 百香果销售活动方案
- 红领巾居家活动方案
- 纪念田汉活动方案
- 骨科显微手外科疾病诊疗规范
- 处方审核培训课件
- 小学教育简历模板
- 学堂在线 研究生的压力应对与健康心理 期末考试答案
- 质检新员工岗位培训内容
- 《党的组织生活制度培训》课件
- 房地产项目营销节点划分及工作安排(范本)
- 2025至2030全球及中国正念冥想应用行业产业运行态势及投资规划深度研究报告
- 联考生物试题及答案大全
- 2025-2030年中国立体车库行业市场发展现状及竞争格局与投资前景研究报告
- 2025-2030年中国睡眠舱行业市场现状供需分析及投资评估规划分析研究报告
评论
0/150
提交评论