c程序设计教程第三章34.docx
- 文档编号:6686632
- 上传时间:2023-01-09
- 格式:DOCX
- 页数:12
- 大小:68.94KB
c程序设计教程第三章34.docx
《c程序设计教程第三章34.docx》由会员分享,可在线阅读,更多相关《c程序设计教程第三章34.docx(12页珍藏版)》请在冰豆网上搜索。
c程序设计教程第三章34
第十六讲:
多重继承
本讲基本要求
*掌握:
继承与派生的概念以及继承的分类。
*理解:
多重派生的构造函数;初始化基类成员的使用。
*了解:
纯虚函数和动态联编。
重点、难点:
继承与派生的概念以及继承的分类。
C++允许一个派生类同时继承多个基类。
这种行为称为多重继承(multipieinheritance)。
一、声明多重继承的方法
如果已声明了类A、类B和类C,可以声明多重继承的派生类D:
classD:
publicA,privateB,protectedC
{类D新增加的成员}
说明:
D是多重继承的派生类,它以公用继承方式继承A类,以私有继承方式继承B类,以保护继承方式继承C类。
D按不同的继承方式的规则继承A,B,C的属性,确定各基类的成员在派生类中的访问权限。
二、多重继承派生类的构造函数
多重继承派生类的构造函数形式:
派生类构造函数名(总参数表列):
基类1构造函数(参数表列),基类2构造函数
(参数表列),基类3构造函数(参数表列)
{派生类中新增数据成员初始化语句}
各基类的排列顺序任意。
派生类构造函数的执行顺序同样为:
先调用基类的构造函数,再执行派生类构造函数的函数体。
例8:
声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的方式声明一个研究生(Graduate)派生类。
教师类中包括数据成员name(姓名)、age(年龄)、title(职称)。
学生类中包括数据成员namel(姓名)、age(性别)、score(成绩)。
在定义派生类对象时给出初始化的数据,然后输出这些数据。
#include
#include
usingnamespacestd;
classTeacher //声明Teacher(教师)类
{ public:
//公用部分
Teacher(stringnam,inta,stringt)//构造函数
{name=nam;age=a;title=t;}
voiddisplay() //输出教师有关数据
{cout<<"name:
"< cout<<"age"< cout<<"title: "< protected: //保护部分 stringname; intage; stringtitle;}; //职称 classStudent //声明类Student(学生) { public: Student(stringnam,chars,floatsco) { name1=nam;sex=s;score=sco;}//构造函数 voiddisplay1() //输出学生有关数据 { cout<<"name: "< cout<<"sex: "< cout<<"score: "< 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: "< cout<<"age: "< cout<<"sex: "< cout<<"score: "< cout<<"title: "< cout<<"wages: "< private: floatwage;}; //工资 intmain() { Graduategrad1("Wang-li",24,'f',"assistant",89.5,1234.5); grad1.show(); return0;} 程序运行结果如下: name: Wang-li age: 24 sex: f score: 89.5 title: assistant wages: 1234.5 由于此程序的目的只是说明多重继承的使用方法,因此对各类的成员尽量简化(例如没有部门、专业、授课名称等项目),以减少篇幅。 有了此基础,读者就可以举一反三,根据实际需要写出更复杂的程序。 注意: 1、由于在两个基类中把数据成员声明为protected,因此可以通过派生类的成员函数引用基类的成员。 如果在基类中把数据成员声明为private,则派生类成员函数不能引用这些数据。 2、出现二义性。 在两个基类中分别用name和namel来代表姓名,其实这是同一个人的名字,从Graduate类的构造函数中可以看到总参数表中的参数nam分别传递给两个基类的构造函数,作为基类构造函数的实参。 现在两个基类都需要有姓名这一项,能否用同一个名字name来代表? 实际上,在本程序中只作这样的修改是不行的,因为在同一个派生类中存在着两个同名的数据成员,在派生类的成员函数show中引用name时就会出现二义性,编译系统无法判定应该选择哪一个基类中的name。 为了解决这个矛盾,程序中分别用name和namel来代表两个基类中的姓名,这样程序能通过编译,正常运行。 但是应该说这是为了通过编译而采用的并不高明的方法。 虽然在本程序中这是可行的,但它没有实用意义,因为绝大多数的基类都是已经编写好的、已存在的,用户可以利用它而无法修改它。 解决这个问题有一个好方法: 在两个基类中可以都使用同一个数据成员名name,而在show函数中引用数据成员时指明其作用域,如cout<<"name: ”< : name< 这就是惟一的,不致引起二义性,能通过编译,正常运行。 3、在多重继承时,从不同的基类中会继承一些重复的数据,例如,在本例中姓名就是重复的,实际上会有更多的重复的数据(如两个基类中都有年龄、性别、住址、电话等),这是很常见的,因为一般情况下使用的是现成的基类。 如果有多个基类,问题会更突出。 在设计派生类时要细致考虑其数据成员,尽量减少数据冗余。 事实上,基类为用户提供了不同的“菜盘子”,用户应当善于选择所需的基类,并对它们作某些加工(选择继承方式),还要善于从基类中选用所需的成员,再加上自己增加的成员,组成自己的“盘子”。 三、多重继承引起的二义性问题 注意的问题: 多重继承增加了程序的复杂度,使程序的编写和维护变得相对困难,容易出错。 其中最常见的问题就是继承的成员同名而产生的二义性(ambiguous)问题。 类A和类B中都有成员函数display和数据成员a,类C是类A和类B的直接派生类。 分别讨论下列3种情况: 情况1: 两个基类有同名成员。 如下图所示。 classA { public: inta; voiddisplay();}; classB {public: inta; voiddisplay();}; classC: publicA,publicB {public: intb; voidshow();}; 如果在main函数中定义C类对象c1,并调用数据成员a和成员函数display: Cc1; c1.a=3: //二义性 c1.display();//二义性 应该怎样解决这个问题呢? 可以用基类名来限定。 解决方案: 如: c1.A: : a=3; //引用cl对象中的基类A的数据成员a c1.A: : display();//调用cl对象巾的基类A的成员函数display 如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以不必写对象名而直接写: A: : a=3; //指当前对象 A: : display(); 如同上一节最后所介绍的那样。 为清楚起见,上图应改用下图的形式表示。 情况2: 两个基类和派生类三者都有同名成员。 将上面的c类声明改为: classC: publicA,publicB { inta; voiddisplay();}; 如右图所示。 即有3个a,3个display函数。 如果在main函数中定义c类对象c1,并调用数据成员a和成员函数display: Ccl; c1.a=3; c1.display(); 解决方案: 说明规则是: 基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。 因此如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。 请注意: 不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。 情况3: 如果类A和类B是从同一个基类派生的如图18 classN {public: inta; voiddisplay(){cout<<"A: :
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 程序设计教程第三章34 程序设计 教程 第三 34