c++程序设计教程第三章34_第1页
c++程序设计教程第三章34_第2页
c++程序设计教程第三章34_第3页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

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

文档简介

第十六讲:多重继承本讲基本要求掌握:继承与派生的概念以及继承的分类。理解:多重派生的构造函数;初始化基类成员的使用。了解:纯虚函数和动态联编。重点、难点:继承与派生的概念以及继承的分类。C++允许一个派生类同时继承多个基类。这种行为称为多重继承(multipieinheritance)。一、声明多重继承的方法如果已声明了类A、类B和类C,可以声明多重继承的派生类D:classD:publicA,privateB,protectedC{类D新增加的成员}说明:D是多重继承的派生类,它以公用继承方式继承A类,以私有继承方式继承BCDA,B,C性,确定各基类的成员在派生类中的访问权限。二、多重继承派生类的构造函数多重继承派生类的构造函数形式:派生类构造函数名(总参数表列):基类1构造函数(参数表列),基2(参数表列),基类3构造函数(参数表列){派生类中新增数据成员初始化语句}各基类的排列顺序任意。派生类构造函数的执行顺序同样为:先调用基类的构造函数,再执行派生类构造函数的函数体。例8:声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的方式声明一个研究生(Graduate)name(age(年龄)、titlenamel(姓名)、age(score(成绩)。在定义派生类对象时给出初始化的数据,然后输出这些数据。#include<iostream>#include<string>usingnamespacestd;classTeacher //声明Teacher{public: //公用部分Teacher(stringnam,inta,stringt)//构造函数{name=nam;age=a;title=t;}voiddisplay() //输出教师有关数据{cout<<"name:"<<name<<endl;cout<<"age"<<age<<endl;cout<<"title:"<<title<<endl;}protected: //保护部分stringname;intage;stringtitle;}; //职称classStudent //Student(学生){public:Student(stringnam,chars,floatsco){name1=nam;sex=s;score=sco;}//构造函数voiddisplay1() //输出学生有关数据{cout<<"name:"<<name1<<endl;cout<<"sex:"<<sex<<endl;cout<<"score:"<<score<<endl;}protected: //保护部分stringname1;charsex;floatscore; };//成绩classGraduate:publicTeacher,publicStudent//Graduate{public:Graduate(stringnam,inta,chars,stringt,floatsco,floatw):Teacher(nam,a,t),Student(nam,s,sco),wage(w){}voidshow()//输出人员的有关数据{cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;cout<<"sex:"<<sex<<endl;cout<<"score:"<<score<<endl;cout<<"title:"<<title<<endl;cout<<"wages:"<<wage<<endl;}private:floatwage;}; //工资intmain(){Graduategrad1("Wang-li",24,'f',"assistant",89.5,1234.5);grad1.show();return0;}程序运行结果如下:name:Wang-liage:24sex:fscore:89.5title:assistantwages:1234.5由于此程序的目的只是说明多重继承的使用方法,因此对各类的成员尽量简化(例如没有部门、专业、授课名称等项目),以减少篇幅。有了此基础,读者就可以举一反三,根据实际需要写出更复杂的程序。注意:1、由于在两个基类中把数据成员声明为protected,因此可以通过派生类的成员函数引用基类的成员。如果在基类中把数据成员声明为private,则派生类成员函数不能引用这些数据。2、出现二义性。在两个基类中分别用namenamelGraduatenamnameshownamename。为了解决这个矛盾,程序中分别用namenamel有一个好方法:在两个基类中可以都使用同一个数据成员名name,而在show数中引用数据成员时指明其作用域,如cout<<"name:”<<Teacher::name<<endl;这就是惟一的,不致引起二义性,能通过编译,正常运行。3、在多重继承时,从不同的基类中会继承一些重复的数据,例如,在本例中姓名就是重复的,实际上会有更多的重复的数据(如两个基类中都有年龄、性别、住址、电话等),这是很常见的,因为一般情况下使用的是现成的基类。需的成员,再加上自己增加的成员,组成自己的“盘子”。三、多重继承引起的二义性问题注意的问题:多重继承增加了程序的复杂度,使程序的编写和维护变得相对困难,容易出错。其中最常见的问题就是继承的成员同名而产生的二义性(ambiguous)问题。ABdisplaya,CAB3情况1:两个基类有同名成员。如下图所示。classA{public:inta;voiddisplay();};classB{public:inta;voiddisplay();};classC:publicA,publicB{public:intb;voidshow();};mainCc1,adisplay:Cc1;c1.a=3://二义性c1.display();//二义性应该怎样解决这个问题呢?可以用基类名来限定。解决方案:如:c1.A::a=3; //clA的数据成员ac1.A::display();//调用cl对象巾的基类A的成员函数display如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以不必写对象名而直接写:A::a=3; //指当前对象A::display();如同上一节最后所介绍的那样。为清楚起见,上图应改用下图的形式表示。情况2:两个基类和派生类三者都有同名成员。将上面的c类声明改为:classC:publicA,publicB{inta;voiddisplay();};3a,3displaymaincc1,adisplay:Ccl;c1.a=3;c1.display();解决方案:说明规则是:基类的同名成员在派生类中被屏蔽,成为“不可见”的,或情况3:如果类A和类B是从同一个基类派生的如图18classN{public:inta;voiddisplay(){cout<<"A::a="<<a<<endl;}};classA:publicN{public:inta1;};classB:publicN{public:inta2;};classC:publicA,publicB{public:inta3;voidshow(){cout<<"a3="<<a3<<endl;}};intmain(){Ccl;//定义C类对象cl„„}ABadisplay,但是它们分NaABadisplay。它们是NABaABNABa图19和图20表示了派生类C中成员的情况。 图19 图20ANc1.a=3;c1.display();c1.N::a=3;c1.N::display();因为这样依然无法区别是类A中从基类N继承下来的成员,还是类B中从基NNN哪一个派牛类中的基类成员。如c1.A::a+3;c1.A::display();//要访问的是类NA员解决方案:四、虚基类1、虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有c1.A::display()。在一个类中保留间接共同基类的多份同名成员,虽然有时是有必要的,可以易出错。而且在实际上,并不需要有多份拷贝。C++提供虚基类(virtualbaseclass)的方法,使得在继承间接共同基类时DBCBCA的派生类,如图2l所示。Adatafun,5.22(aBCAdatafun,Bdatab,data_c5.22(b)DAdata5.22(c)中表示为intB::dataintC::datavoidB::fun()voidBdata_bCdatacDdata_d图21 图22现在,将类A声明为虚基类,方法如下:classA //A{„};classB:virtualpublicA//B是类AA是B虚基类{„};classC:virtualpublicA//声明类CAAC的虚基类{„};注意:虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。声明虚基类的一般形式为:class派生类名:virtual继承方式基类名即在声明派生类时,将关键字virtual加到相应的继承方式前面。经过这样类一次,也就是说,基类成员只保留一次。BCD所示。需要注意:为了保证虚基类在派生类中只继才一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。如果像图5.24BCADAEBcD 图23 图242、虚基类的初始化如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则表对虚基类进行初始化。例如:classA //A{A(inti){}//基类构造函数,有一个参数...};classB:virtualpublicA//A作为B的虚基类{B(intn):A(n){} //B初始化„};classC:virtualpublicA//A作为C的虚基类{C(intn):A(n)){} //C初始化„};classC//类D有基类初始化{D(intn):A(n),B(n),C(n){}„};注意:1、在定义类D的构造函数时,与以往使用的方法有所不同。规定:在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。2、C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类c)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。3、虚基类的简单应用举例98TeacherStudentPerson25Person类和Student类中再增加一些必要的数据。#include<iostream>#include<string>usingnamespacestd;1、定义公共基类PersonclassPerson{public:Person(char*nam,chars,inta)//构造函数{strcpy(name,nam);sex=s;age=a;}protected: //charname[20];charsex;intage;2、定义类TeacherclassTeacher:virtualpublicPerson//声明Person为公用继承的虚基类{public:Teacher(char*nam,chars,inta,char*t):Person(nam,s,a)//构造函数{strcpy(title,t);}protected: //chartitle[10];};//职称3、定义类StudentclassStudent:virtualpublicPerson//声明Person为公用继承的虚基类{public:Student(char*nam,chars,inta,floatsco)://构造函Person(nam,s,a),score(sco){} //初始化表protected: //保护成员floatscore;};//成绩4、定义多重继承的派生类GraduateclassGraduate:publicTeacher,publicStudent//声明TeacherStudent类为公用继承直接基类{public:Graduate(char*nam,chars,inta,char*t,floatsco,floatw)://构造函数Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}//初始化表voidshow()//输出研究生的有关数据{cout<<"name:"<<name<<endl;cout<<"age:"<<

温馨提示

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

评论

0/150

提交评论