C++电子课件(下)第八章.ppt
《C++电子课件(下)第八章.ppt》由会员分享,可在线阅读,更多相关《C++电子课件(下)第八章.ppt(116页珍藏版)》请在三一办公上搜索。
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、承。,(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,man,woman;int Birthda
8、y;/生日,格式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;/提取性别void SetId(string id)
9、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
10、 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 c
11、ourse string coursename;int grade;,验证主函数,8.2 派生类的构造函数与析构函数,注意:本例中标准C+字符串string是作为成员对象使用的(聚合),动态内存分配的构造和析构被封装起来,使用十分简单。如使用动态生成的C风格字符串,要考虑深复制,那要复杂得多。提倡完善的类对象封装,不仅封装数据和对数据的操作,而且封装资源的动态分配与释放,形成一个完备的子系统。在一个有层次结构的类体系中资源的动态分配与释放应封装在成员对象中,如同使用标准的string字符串类那样。聚合是一种完善的封装。采用成员对象将大大简化层次结构的类体系中资源的动态分配与释放的处理方法,不再
12、出现难度极大的多层次的深复制。,8.1.2 公有派生与私有派生,访问限定符讨论:派生类成员(新增成员)函数对基类(继承来的)成员的访问(调用和操作),和从派生类对象之外对派生类对象中的基类成员的访问。下面进行详细讨论:,8.1.2 公有派生与私有派生,公有派生是绝对主流。,8.1.2 公有派生与私有派生,保护派生:直接派生:基类的私有成员仍是派生类的私有成员,不可直接访问,而基类中的公有和保护成员全部成为派生类的保护成员,在派生类中可以直接访问。但在派生类对象之外是不能直接访问该对象的任何基类成员的。与私有派生相同。多层派生:把保护派生类作为基类或把私有派生类作为基类再作一层保护派生。在新的保
13、护派生类中可直接访问由保护派生传递过来的底层基类的公有和保护成员,而不可直接访问由私有派生传递来的底层基类的公有和保护成员。但在该类对象之外都不可直接访问类对象底层基类的公有成员。合理使用保护限定方式可以在复杂的类层次关系中取一个共享访问和成员封装隐蔽性的折衷。,派生类构造函数的定义:派生类名:派生类名(参数总表):基类名1(参数名表1),基类名2(参数名表2),基类名n(参数名表n),成员对象名1(成员对象参数名表1),成员对象名m(成员对象参数名表m)/派生类新增成员的初始化;/所列出的成员对象名全部为新增成员对象的名字,注意:在构造函数的声明中,冒号及冒号以后部分必须略去。所谓不能继承并
14、不是不能利用,而是把基类的构造函数作为新的构造函数的一部分,或者讲调用基类的构造函数。基类名仅指直接基类,写了底层基类,编译器认为出错。冒号后的基类名,成员对象名的次序可以随意,这里的次序与调用次序无关。,8.2 派生类的构造函数与析构函数,派生类构造函数各部分执行次序:1.调用基类构造函数,按它们在派生类定义的先后顺序,顺序调用。2.调用成员对象的构造函数,按它们在类定义中声明的先后顺序,顺序调用。3.派生类的构造函数体中的操作。,8.2 派生类的构造函数与析构函数,注意:在派生类构造函数中,只要基类不是使用无参的默认构造函数都要显式给出基类名和参数表。如果基类没有定义构造函数,则派生类也可
15、以不定义,全部采用系统给定的默认构造函数。如果基类定义了带有形参表的构造函数时,派生类就应当定义构造函数。,8.2 派生类的构造函数与析构函数,析构函数:析构函数的功能是作善后工作。只要在函数体内把派生类新增的一般成员处理好就可以了,而对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。析构函数各部分执行次序与构造函数相反,首先对派生类新增一般成员析构,然后对新增对象成员析构,最后对基类成员析构。,8.3 多重继承与派生类成员标识(选读),由多个基类共同派生出新的派生类,这样的继承结构被称为多重继承或多继承(multiple-inheritance),多重继承实例:
16、,图8.3 大学在册人员继承关系,8.3 多重继承与派生类成员标识(选读),派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构。,8.3 多重继承与派生类成员标识(选读),歧义性问题:参见图8.3,比如行政人员兼教师,在其基类教师中有一个“教职工编号”,另一基类行政人员中也有一个“教职工编号”,如果只讲教职工编号那么是哪一个基类中的呢?这两者可能是一回事,但计算机系统并不这么认为。进一步,如果“教职工编号”是由两个基类“教师”和“行政人员”共同的基类“教职工”类继承来的,只有同一个标识符,也不能用改标识符来区分。,唯一标识问题:通常采用作用域分辨符“:”:基类名:成员
17、名;/数据成员基类名:成员名(参数表);/函数成员,图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/身份证号,两个身份证号从逻辑上讲应是一回事,但是物理上是分配了不同内存空间,是两个变量
18、,请参见图8.4(b)。,图8.4(b)在职研究生派生类存储图,建议采用有确定字面意思的标识符,它可以被编译器简单区分出来。如果class Person的身份证号标识为int IdPerson,则写为:EGStud1.GStudent:IdPersonEGStud1.Employee:IdPerson不必标出那么多层次的类,但写EGStud1:IdPerson是错的。作用域分辨符不能嵌套使用,如:EGStud1.GStudent:Student:No/学生号EGStud1.GStudent:Student:Person:No/身份证号是错误的。,8.3 多重继承与派生类成员标识(选读),8.3
19、 多重继承与派生类成员标识(选读),一般数据成员总是私有成员,派生类对基类的访问只能间接进行。访问身份证号,应通过class Person中的公有成员函数(接口)GetNo()和SetNo()进行:EGStud1.Employee.Person:SetNo(no);no=EGStud1.Employee.Person:GetNo();,注意:,【例8.2】由圆和高多重继承派生出圆锥。因为公有派生时,在派生类中不可以直接访问基类的私有成员,但可以直接访问基类的保护成员,当需要在派生类中访问基类的数据成员时,可以将它们定义为保护的,而不是私有的。本例中类Circle为圆;类Line为高;类Cone
20、为圆锥,由Circle和Line公有派生而来。在Cone类中,Circle和Line类的接口完全不变,可以直接调用,这就是公有派生的优点。在Cone的成员函数中可直接访问Circle和Line中的公有成员和保护成员。,【例8.2】由圆和高多重继承派生出圆锥,检证主程序:,圆类Circle定义,高类Line定义,圆锥类Cone定义,虚基类的引入:在图8.4中,两个身份证号显然是不合理的。可以把class Person这个共同基类设置为虚基类,这样就仅有一个Person基类成员,从不同路径继承来的同名数据成员(身份证号)在内存中就是同一个数据。,8.4 虚基类(选读),注意:virtual 关键字
21、只对紧随其后的基类名起作用:class Student:virtual public Person.;class Employee:virtual public Person.;,虚基类(virtual base class)定义:class 派生类名:virtual 访问限定符 基类类名.;class 派生类名:访问限定符 virtual 基类类名.;,8.4 虚基类(选读),这种继承称为虚拟继承,虚拟继承:,在Person的位置上放的是指针,两个指针都指向Person成员存储的内存。这种继承称为虚拟继承(virtual inheritance)。,8.4 虚基类(选读),派生类名:派生类名
22、(参数总表):基类名1(参数名表1),基类名2(参数名表2),基类名n(参数名表n),成员对象名1(成员对象参数名表1),成员对象名m(成员对象参数名表m),底层虚基类名1(参数名表1),底层虚基类名r(参数名表r)/派生类新增成员的初始化;/所列出的成员对象名全部为新增成员对象的名字在多层虚拟继承构造函数中,基类名不仅要列出直接基类,而且要列出底层虚基类,否则编译器认为出错。如不是虚拟继承只能列直接基类。,虚拟继承的构造函数:,8.4 虚基类(选读),在派生类对象的创建中:首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构造函数按它们声明的顺序调用。第三批是成员对象的构造函数
23、。最后是派生类自己的构造函数被调用。,构造函数执行次序:,8.4 虚基类(选读),【例8.3】在采用虚基类的多重继承中构造与析构的次序。,class Objectpublic:Object()coutconstructor Objectn;Object()coutdeconstructor Objectn;class Bclass1public:Bclass1()coutconstructor Bclass1n;Bclass1()coutdeconstructor Bclass1n;class Bclass2public:Bclass2()coutconstructor Bclass2n;Bc
24、lass2()coutdeconstructor Bclass2n;,8.4 虚基类(选读),【例8.3】在采用虚基类的多重继承中,构造与析构的次序。,class Bclass3public:Bclass3()coutconstructor Bclass3n;Bclass3()coutdeconstructor Bclass3n;class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2 Object object;public:Dclass():object(),Bclass2(),Bclass3(),Bclass1()cout派生
25、类建立!n;Dclass()cout派生类析构!n;int main()Dclass dd;cout“主程序运行!n”;return 0;,运行结果:Constructor Bclass3/第一个虚拟基类,与派生类析构函数排列无关Constructor Bclass2/第二个虚拟基类Constructor Bclass1/非虚拟基类Constructor Object/对象成员派生类建立!主程序运行!派生类析构!deconstructor Object/析构次序相反deconstructor Bclass1deconstructor Bclass2deconstructor Bclass3/析
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 电子 课件 第八
链接地址:https://www.31ppt.com/p-6153966.html