ImageVerifierCode 换一换
格式:DOCX , 页数:12 ,大小:68.93KB ,
资源ID:7197132      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7197132.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(c++程序设计教程第三章34.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

c++程序设计教程第三章34.docx

1、c+程序设计教程第三章34第十六讲:多重继承本讲基本要求* 掌握:继承与派生的概念以及继承的分类。* 理解:多重派生的构造函数;初始化基类成员的使用。* 了解:纯虚函数和动态联编。重点、难点:继承与派生的概念以及继承的分类。 C+允许一个派生类同时继承多个基类。这种行为称为多重继承(multipie inheritance)。一、声明多重继承的方法如果已声明了类A、类B和类C,可以声明多重继承的派生类D:class D:public A,private B,protected C 类D新增加的成员 说明:D是多重继承的派生类,它以公用继承方式继承A类,以私有继承方式继承B类,以保护继承方式继承

2、C类。D按不同的继承方式的规则继承A,B,C的属性,确定各基类的成员在派生类中的访问权限。二、多重继承派生类的构造函数多重继承派生类的构造函数形式:派生类构造函数名(总参数表列):基类1构造函数(参数表列),基类2构造函数(参数表列),基类3构造函数(参数表列)派生类中新增数据成员初始化语句各基类的排列顺序任意。派生类构造函数的执行顺序同样为:先调用基类的构造函数,再执行派生类构造函数的函数体。例8:声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的方式声明一个研究生(Graduate)派生类。教师类中包括数据成员name(姓名)、age(年龄)、title(职称)。

3、学生类中包括数据成员namel(姓名)、age(性别)、score(成绩)。在定义派生类对象时给出初始化的数据,然后输出这些数据。#include #include using namespace std;class Teacher /声明Teacher(教师)类public: /公用部分Teacher(string nam,int a,string t) /构造函数 name=nam; age=a; title=t;void display() /输出教师有关数据 coutname:nameendl;coutageageendl;couttitle:titleendl; protected:

4、 /保护部分string name;int age;string title; /职称class Student /声明类Student(学生)public:Student(string nam,char s,float sco)name1=nam; sex=s; score=sco; /构造函数void display1() /输出学生有关数据coutname:name1endl;coutsex:sexendl;coutscore:scoreendl; protected: /保护部分 string name1;char sex;float score;; /成绩class Graduate

5、:public Teacher,public Student /声明多重继承的派生类Graduatepublic:Graduate(string nam,int a,char s,string t,float sco,float w):Teacher(nam,a,t),Student(nam,s,sco),wage(w) void show( ) /输出人员的有关数据coutname:nameendl;coutage:ageendl;coutsex:sexendl;coutscore:scoreendl;couttitle:titleendl;coutwages:wageendl; priva

6、te:float wage; ;/工资int main( )Graduate grad1(Wang-li,24,f,assistant,89.5,1234.5);grad1.show( );return 0; 程序运行结果如下:name:Wang-liage:24sex:fscore:89.5title:assistantwages:12345由于此程序的目的只是说明多重继承的使用方法,因此对各类的成员尽量简化(例如没有部门、专业、授课名称等项目),以减少篇幅。有了此基础,读者就可以举一反三,根据实际需要写出更复杂的程序。注意:1、由于在两个基类中把数据成员声明为protected,因此可以通

7、过派生类的成员函数引用基类的成员。如果在基类中把数据成员声明为private,则派生类成员函数不能引用这些数据。2、出现二义性。在两个基类中分别用name和namel来代表姓名,其实这是同一个人的名字,从Graduate类的构造函数中可以看到总参数表中的参数nam分别传递给两个基类的构造函数,作为基类构造函数的实参。现在两个基类都需要有姓名这一项,能否用同一个名字name来代表?实际上,在本程序中只作这样的修改是不行的,因为在同一个派生类中存在着两个同名的数据成员,在派生类的成员函数show中引用name时就会出现二义性,编译系统无法判定应该选择哪一个基类中的name。为了解决这个矛盾,程序中

8、分别用name和namel来代表两个基类中的姓名,这样程序能通过编译,正常运行。但是应该说这是为了通过编译而采用的并不高明的方法。虽然在本程序中这是可行的,但它没有实用意义,因为绝大多数的基类都是已经编写好的、已存在的,用户可以利用它而无法修改它。解决这个问题有一个好方法:在两个基类中可以都使用同一个数据成员名name,而在show函数中引用数据成员时指明其作用域, 如 coutname:”Teacher:nameendl;这就是惟一的,不致引起二义性,能通过编译,正常运行。3、在多重继承时,从不同的基类中会继承一些重复的数据,例如,在本例中姓名就是重复的,实际上会有更多的重复的数据(如两个基

9、类中都有年龄、性别、住址、电话等),这是很常见的,因为一般情况下使用的是现成的基类。如果有多个基类,问题会更突出。在设计派生类时要细致考虑其数据成员,尽量减少数据冗余。事实上,基类为用户提供了不同的“菜盘子”,用户应当善于选择所需的基类,并对它们作某些加工(选择继承方式),还要善于从基类中选用所需的成员,再加上自己增加的成员,组成自己的“盘子”。三、多重继承引起的二义性问题注意的问题:多重继承增加了程序的复杂度,使程序的编写和维护变得相对困难,容易出错。其中最常见的问题就是继承的成员同名而产生的二义性(ambiguous)问题。类A和类B中都有成员函数display和数据成员a,类C是类A和类

10、B的直接派生类。分别讨论下列3种情况:情况1:两个基类有同名成员。如下图所示。class A public:int a;void display();class B public:int a;void display(); ;class C:public A,public B public:int b;void show();如果在main函数中定义C类对象c1,并调用数据成员a和成员函数display:C c1;c1.a=3:/二义性c1.display();/二义性应该怎样解决这个问题呢?可以用基类名来限定。解决方案:如:c1.A:a=3; /引用cl对象中的基类A的数据成员ac1.A:d

11、isplay(); /调用cl对象巾的基类A的成员函数display如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以不必写对象名而直接写:A:a=3; /指当前对象A:display();如同上一节最后所介绍的那样。为清楚起见,上图应改用下图的形式表示。情况2:两个基类和派生类三者都有同名成员。将上面的c类声明改为:class C:public A,public Bint a;void display(); ;如右图所示。即有3个a,3个display函数。如果在main函数中定义c类对象c1,并调用数据成员a和成员函数display:C cl;c1. a=3;

12、c1.display();解决方案:说明规则是:基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。因此如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。请注意:不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。情况3:如果类A和类B是从同一个基类派生的如图18class N public: int a;void display()coutA:a=aendl; ;class A:public N public: int a1

13、; ;class B:public Npublic:int a2; ;class C:public A,public B public: int a3;void show()couta3=a3endl; ;int main() C cl;/定义C类对象cl在类A和类B中虽然没有定义数据成员a和成员函数display,但是它们分别从类N继承了数据成员a和成员函数display,这样在类A和类B中同时存在着两个同名的数据成员a和成员函数display。它们是N类成员的拷贝。类A和类B中的数据成员a代表两个不同的存储单元,可以分别存放不同的数据。在程序中可以通过类A和类B的构造函数去调用基类N的构造

14、函数,分别对类A和类B的数据成员a初始化。图19和图20表示了派生类C中成员的情况。图 19 图 20怎样才能访问类A中从基类N继承下来的成员呢?显然不能用c1.a=3;c1.display(); 或 c1.N:a=3;c1.N:display();因为这样依然无法区别是类A中从基类N继承下来的成员,还是类B中从基类N继承 下来的成员。应当通过类N的直接派生类名来指出要访问的是类N的哪一个派牛类中的基类成员。如c1.A:a+3; c1.A:display(); /要访问的是类N的派生类A中的基类成员解决方案:四、虚基类1、 虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接

15、基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如c1A:display()。在一个类中保留间接共同基类的多份同名成员,虽然有时是有必要的,可以在不同的 数据成员中分别存放不同的数据,也可以通过构造函数分别对它们进行初始化。但在大多数情况下,这种现象是人们不希望出现的。因为保留多份数据成员的拷贝,不仅占用较多的存储空间,还增加了访问这些成员时的困难,容易出错。而且在实际上,并不需要有多份拷贝。C+提供虚基类(virtual base class)的方法,使

16、得在继承间接共同基类时只保留一份成员。假设类D是类B和类C公用派生类,而类B和类C又是类A的派生类,如图2l所示。设类A有数据成员data和成员函数fun,见图522(a)。派生类B和C分别从类A继承了data和fun,此外类B还增加了自己的数据成员data b,类C增加了数据成员data_c。如图 522(b)所示。如果不用虚基类,根据前面学过的知识,在类D中保留了类A成员data的两份拷贝。在图522(c)中表示为intB:data和int C:data。同样有两个同名的成员函数,表示为void B:fun()和void C:fun()。类B中增加的成员data_b和类C中增加的成员dat

17、a c不同名,不必用类名限定。此外,类D还增加了自己的数据成员data_d和成员函数fun_d。图 21 图 22现在,将类A声明为虚基类,方法如下:class A /声明基类A;classB:virtual publicA /声明类B是类A的公用派生类,A是B的虚基类;classC:virtual public A /声明类C是类A的公用派生类,A是C的虚基类;注意:虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声 明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。声明虚基类的一般形式为:class派生类名:virtual 继承方式

18、基类名即在声明派生类时,将关键字virtual加到相应的继承方式前面。经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次,也就是说,基类成员只保留一次。在派生类B和C中作了上面的虚基类声明后,派生类D中的成员如图523所示。需要注意:为了保证虚基类在派生类中只继才一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。如果像图524所示的那样,在派生类B和C中将类A声明为虚基类,而在派生类D中没有将类A声明为虚基类,则在派生类E中,虽然从类B和c路径派生的部分只保留一份基类成员,但从类D路径派生的部分还保留一份基类成员。图23 图24

19、2、虚基类的初始化如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类(包括直接派生或间接派生的派生类)中,通过构造函数的初始化表对虚基类进行初始化。例如:class A /定义基类AA(int i)/基类构造函数,有一个参数.;class B:virtual public A /A作为B的虚基类B(int n):A(n)/B类构造函数,在初始化表中对虚基类初始化;class C:virtual publicA /A作为C的虚基类C(int n):A(n) /C类构造函数,在初始化表中对虚基类初始化;class D:public B,public C /类D的构造函

20、数,在初始化表中对所有基类初始化D(int n):A(n),B(n),C(n);注意:1、在定义类D的构造函数时,与以往使用的方法有所不同。规定:在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。2、C+编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类c)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。3、虚基类的简单应用举例例9 在例8的基础上,在Teacher类和Student类之上增加一个共同的基类Person,如图25所示。作为人员的一些基本数据都放在Person中,在Teacher类和Student

21、类中再增加一些必要的数据。#include #include using namespace std;1、定义公共基类Personclass Person public:Person(char *nam,char s,int a) /构造函数 strcpy(name,nam);sex=s;age=a;protected: /保护成员char name20;char sex;int age; ;2、定义类Teacherclass Teacher:virtual public Person /声明Person为公用继承的虚基类public: Teacher(char *nam,char s,int

22、 a,char *t):Person(nam,s,a) /构造函数 strcpy(title,t); protected: /保护成员char title10; ; /职称3、定义类Studentclass Student:virtual public Person /声明Person为公用继承的虚基类public:Student(char *nam,char s,int a,float sco): /构造函数Person(nam,s,a),score(sco) /初始化表protected: /保护成员float score;/成绩4、定义多重继承的派生类Graduateclass Grad

23、uate:public Teacher,public Student /声明Teacher和Student类为公用继承直接基类public:Graduate(char *nam,char s,int a,char *t,float sco,float w): /构造函数Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w) /初始化表void show( ) /输出研究生的有关数据coutname:nameendl;coutage:ageendl;coutsex:sexendl;coutscore:scoreendl;co

24、uttitle:titleendl;coutwages:wageendl; private:float wage; ; /工资int main( )Graduate grad1(Wang-li,f,24,assistant,89.5,1234.5);grad1.show( );return 0; 运行结果为name:Wang-liage:24Sex:Tscore:895title:assistantwages:12345说明:1、Person类是表示一般人员属性的公用类,其中包括人员的基本数据,现在只包含了3个数据成员:name(姓名)、sex(性别)、age(年龄)。Teacher和Stud

25、ent类是Person的公用派生类,在Teacher类中增加title(职称),在Student类中增加score(成绩)。Graduate(研究生)是Teacher类和Student类的派生类,在Graduate类中增加wage(津贴)。一个研究生应当包含以上全部数据。为简化程序,除了最后的派生类Graduate外,在其他类中均不包含成员函数。2、清注意各类的构造函数的写法。在Person类中定义了包含3个形参的构造函 数,用它对数据成员name、sex和age进行初始化。在Teacher和Student类的构造函数中,按规定要在初始化表中包含对基类的初始化,尽管对虚基类来说,编译系统不会由

26、此调用基类的构造函数,但仍然应当按照派生类构造函数的统一格式书写。在最后派生类Graduate的构造函数中,既包括对虚基类构造函数的调用,也包括对其直接基类的初始化。3、在Graduate类中,只保留一份基类的成员,因此可以用Graduate类中的show函数引用Graduate类对象中的公共基类Person的数据成员name,sex,age的值,不需要加基类名和域运算符(:),不会产生二义性。可以看到: 使用多重继承时要十分小心,经常会出现二义性问题。前面介绍的例子是简单的,如果派生的层次再多一些,多重继承更复杂一些,程序设计人员很容易陷入迷魂阵,程序的编写、调试和维护工作都会变得更加困难。因此,许多专业人员认为:不要提倡在程序中使用多重继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多重继承,能用单一继承解决的问题就不要使用多重继承。也是由于这个原因,有些面向对象的程序设计语言(如Java,Smalltalk)并不支持多重继承。五、作业:1、P196: 8、9题。2、实验6。

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1