




已阅读5页,还剩52页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第四章继承和派生 1 4 1继承和派生的概念 面向对象程序设计有4个主要特点 抽象 封装 继承和多态性 在本章中主要介绍有关继承的知识 在下一章中将介绍多态性 C 语言提供了类的继承机制 解决了软件重用问题 4 1 1继承与派生的概念 一个类中包含了若干数据成员和成员函数 在不同的类中 数据成员和成员函数是不相同的 但有时两个类的内容基本相同或有一部分相同 2 类的继承 一个新类从已存在的类那里获得该类已有的特性叫作类的继承 已存在的类叫作父类 也叫作基类 产生的新类叫作子类或派生类 类的派生 从一个已有的类那里产生一个新类的过程叫类的派生 已存在的类叫作父类 也叫作基类 产生的新类叫作派生类或子类 类的继承和派生是同一概念 前者是从子类的角度来说 后者是从父类的角度来说的 我们通常说子类继承了父类 父类派生了子类 3 描述各级学生的类的继承关系如下图 基类与派生类的关系 派生类是基类的具体化 基类则是派生类的抽象一个派生类的对象也是一个基类的对象 应该具有基类的一切属性和方法 派生类除了具有基类的一切属性和方法外 还可以有自己所特有的属性和方法 4 1 2派生类和基类的关系 4 4 1 3单继承与多继承 单继承 一个派生类只从一个基类继承 多重继承 一个派生类从两个或多个基类继承 5 4 2派生类的声明方式 声明派生类的一般形式为class派生类名 继承方式 基类名 派生类新增加的成员 继承方式包括 public 公有的 private 私有的 和protected 受保护的 此项是可选的 如果不写此项 则默认为private 私有的 如下程序演示了类Rectangle 四边形 由类Point继承而来 6 voidSetPoint intx inty this x x this y y voidMovePoint intdx intdy x dx y dy voidShowPoint cout x y Point h include includeusingnamespacestd classPoint private intx y public intGetX returnx intGetY returny 7 voidSetRect intx inty intw inth SetPoint x y width w height h voidShowRect cout 左上角坐标为 ShowPoint cout endl cout 宽为 width endl cout 长为 height endl Rectangle h include include Point h usingnamespacestd classRectangle publicPoint private intwidth intheight public intGetWidth returnwidth intGetHight returnheight 8 main cpp include Rectangle h voidmain Rectangler r SetRect 0 0 10 20 r ShowRect r MovePoint 10 10 r ShowRect 9 4 3派生类的构成 派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分 在基类中包括数据成员和成员函数 或称数据与方法 两部分 派生类分为两大部分 一部分是从基类继承来的成员 另一部分是在声明派生类时增加的部分 每一部分均分别包括数据成员和成员函数 10 如果在派生类中定义了和基类中同名函数 函数参数个数和类型可以相同也可以不相同 则派生类中的函数会隐藏基类的同名函数 在派生类中不能直接访问基类中的同名函数 注意与重载的区别 在同一个类中的同名不同参函数为重载函数 如程序PointRect1所示 11 4 4继承方式 派生类的继承方式有三种 public private protected 不同的继承方式决定了基类成员在派生类中的访问属性 12 4 4 1类的保护成员 前面介绍过类的成员 数据成员和成员函数 的访问属性有私有的 private 的和公有的 public的 另外还提到类的访问属性也可以有保护的 protected的 类中的protected成员与private成员一样 只能在本类的成员函数中访问 不能在类外通过对象来访问 但通过上面的表中可以看出当类派生时 基类的private成员在派生类中是不可访问的 而基类的protected成员在派生类中随继承方式的不同而不同 13 classDrived publicBase protected intj public voidFun i 20 includeusingnamespacestd classBase protected inti public voidF voidmain Drivedd 14 4 5派生类的构造函数和析构函数 构造函数的主要作用是对数据成员初始化 在设计派生类的构造函数时 不仅要考虑派生类所增加的数据成员的初始化 还应当考虑基类的数据成员初始化 也就是说 希望在执行派生类的构造函数时 使派生类的数据成员和基类的数据成员同时都被初始化 解决这个问题的思路是 在执行派生类的构造函数时 调用基类的构造函数 注意 派生类继承基类的除构造函数和析构函数以外的所有函数 15 4 4 1简单的派生类的构造函数 简单的派生类 只有一个基类 而且只有一级派生 只有直接派生类 没有间接派生类 在派生类的数据成员中不包含基类的对象 即子对象 简单派生类中我们一般采用在派生类的构造函数初始化列表中调用基类的构造函数来对继承基类的数据成员进行初始化 其一般形式为 派生类构造函数名 总参数表列 基类构造函数名 参数表列 派生类中新增数据成员初始化语句 简单派生类的构造函数的形式 16 include includeusingnamespacestd classStudent public Student intn stringnam chars num n name nam sex s Student protected intnum stringname charsex 17 classStudent1 publicStudent public Student1 intn stringnam chars inta stringad Student n nam s age a addr ad voidshow cout num num endl cout name name endl cout sex sex endl cout age age endl cout address addr endl endl Student1 private intage stringaddr 18 在main函数中 建立对象stud1时指定了5个实参 它们按顺序传递给派生类构造函数Student1的形参 然后 派生类构造函数将前面3个传递给基类构造函数的形参 19 简单派生类的构造函数的几点说明 a 定义派生类的对象时系统自动调用派生类构造函数之前会先调用其基类的构造函数 基类的构造函数是在派生类的构造函数的初始化列表中给出 如果在初始化列表中没显式给出调用语句则调用基类的默认构造函数 例如前例中派生类的构造函数为 Student1 intn stringnam chars inta stringad Student n nam s 如果在main函数中定义一个Student1类的对象时 系统会先调用基类的构造函数 然后执行Student1的构造函数体内的代码完成对派生类成员的构造 如果Student1类的构造函数改为Student1 intn stringnam char 则会先调用基类的默认构造函数 20 当派生类构造函数在类外定义时 则只在类外的函数定义处加上调用基类的初始化列表 在类内申明的地方不加 由前面例题中的构造函数Student1 intn stringnam chars inta stringad Student n nam s 可以看出派生类的构造函数的初始化列表中是在调用基类的构造函数而不是在申明或定义基类的构造函数 所以Student1中的五个参数是形参 带参数类型 而其初始化列表中的Student的三个参数是实参 不带有类型 这些实参取自Student1 所以Studen中的三个参数也可以为常数 例如可将Student1的构造函数改为 Student1 stringnam chars inta stringad Student 10010 nam s 在派生类对象释放时先执行派生类的析构函数 然后执行基类的析构函数 21 例题 定义一个点类Point 由Point派生出一个圆类Circle Point h文件 ifndefPOINT H definePOINT HclassPoint protected floatx floaty public Point x 0 y 0 Point floatx floaty voidShow endif Point cpp文件 include include Point h usingnamespacestd Point Point floatx floaty this x x this y y voidPoint Show cout x y endl 22 Circle h文件 ifndefCIRCLE H defineCIRCLE H include include Point h usingnamespacestd classCircle publicPoint protected floatr public Circle floatx floaty floatr voidShow floatGetArea floatGetLength endif Circle cpp文件 include include CirCle h usingnamespacestd Circle Circle floatx floaty floatr Point x y this r r voidCircle Show cout 圆心为 Point x y Show cout 半径为 r endl floatCircle GetArea return3 14159 r r floatCircle GetLength return3 14159 2 r 23 4 5 2有子对象的派生类的构造函数 类的数据成员中还可以包含类对象 例如前面的Student1类继承自Student类 我们可以在Student1类中加入一个Student类的对象来表示该同学所在班的班长 如下程序所示 include includeusingnamespacestd classStudent public Student intn stringnam num n name nam voiddisplay cout num num endl name name endl protected intnum stringname 24 classStudent1 publicStudent public Student1 intn stringnam intn1 stringnam1 inta stringad Student n nam monitor n1 nam1 age a addr ad voidshow cout 学生信息为 endl display cout age age endl cout address addr endl cout 班长为 endl monitor display private Studentmonitor intage stringaddr 25 intmain Student1stud1 10010 Wang li 10001 Li sun 19 115BeijingRoad Shanghai stud1 show return0 派生类构造函数的任务应该包括3个部分 1 对基类数据成员初始化 2 对子对象数据成员初始化 3 对派生类数据成员初始化 其中前两个必须放在派生类的构造函数的初始化列表中进行 第 3 个可以在函数体中也可以在初始化列表中进行 26 定义派生类构造函数的一般形式为派生类构造函数名 总参数表列 基类构造函数名 参数表列 子对象名 参数表列 派生类中新增数成员据成员初始化语句 执行派生类构造函数的顺序是 调用基类构造函数 对基类数据成员初始化 调用子对象构造函数 对子对象数据成员初始化 再执行派生类构造函数本身 对派生类数据成员初始化 以上次序是固定的 不会因为基类构造函数调用写在前面还是子对象名写在前面而改变 27 例题 定义一个点类Point 由Point派生出一个圆类Circle Point h文件 ifndefPOINT H definePOINT HclassPoint protected floatx floaty public Point x 0 y 0 Point floatx floaty voidShow endif Point cpp文件 include include Point h usingnamespacestd Point Point floatx floaty this x x this y y voidPoint Show cout x y endl 4 5 3多层派生时的构造函数 28 Circle h文件 ifndefCIRCLE H defineCIRCLE H include include Point h usingnamespacestd classCircle publicPoint protected floatr public Circle floatx floaty floatr voidShow floatGetArea floatGetLength endif Circle cpp文件 include include CirCle h usingnamespacestd Circle Circle floatx floaty floatr Point x y this r r voidCircle Show cout 圆心为 Point x y Show cout 半径为 r endl floatCircle GetArea return3 14159 r r floatCircle GetLength return3 14159 2 r 29 column h ifndefCOLUMN H defineCOLUNM H include Circle h classColumn publicCircle private floath public Column floatx floaty floatr floath Circle x y r this h h voidShow floatGetArea floatGetVolume endif 30 Column cpp include Column h includeusingnamespacestd voidColumn Show Circle Show cout 高为 h endl floatColumn GetArea return2 Circle GetArea GetLength h floatColumn GetVolume returnCircle GetArea h 31 在多层派生的情况下 派生类的构造函数初始化列表中只须写出其上一层派生类的构造函数 不要再写上其间接子类的构造函数 32 4 5 4派生类的析构函数 在派生时 派生类是不能继承基类的析构函数的 也需要通过派生类的析构函数去调用基类的析构函数 在派生类中可以根据需要定义自己的析构函数 用来对派生类中所增加的成员进行清理工作 基类的清理工作仍然由基类的析构函数负责 在执行派生类的析构函数时 系统会自动调用基类的析构函数和子对象的析构函数 对基类和子对象进行清理 调用的顺序与构造函数正好相反 先执行派生类自己的析构函数 对派生类新增加的成员进行清理 然后调用子对象的析构函数 对子对象进行清理 最后调用基类的析构函数 对基类进行清理 33 4 6多重继承 前面讨论的是单继承 即一个类是从一个基类派生而来的 实际上 常常有这样的情况 一个派生类有两个或多个基类 派生类从两个或多个基类中继承所需的属性 C 为了适应这种情况 允许一个派生类同时继承多个基类 这种行为称为多重继承 multipleinheritance 4 6 1声明多重继承的方式 声多重继承子类的方法和单继承相似 只是在标明子类的位置将继承的父类都写上 且以豆号隔开 例如类 多重继承了类A B C则申明类 的方法如下 classD publicA protectedB privateC 类 新增加的成员 34 多重继承的子类具有多个父类 子类中具有所有父类的所有成员 且对多个父类可以有不同的继承方式 不同的继承方式决定了继承而来的父类的成员在子类中的访问属性的不同 4 6 2多重继承的派生类的构造函数 多重继承派生类的构造函数形式与单继承时的构造函数形式基本相同 只是在初始列表中包含多个基类构造函数 形式如下 派生类构造函数名 总参数表列 基类1构造函数 参数表列 基类2构造函数 参数表列 基类3构造函数 参数表列 派生类中新增数据成员成员初始化语句 35 派生类构造函数的执行顺序同样为 先调用基类的构造函数 再执行派生类构造函数的函数体 调用基类构造函数的顺序是按照声明派生类时基类出现的顺序 与构造函数初始化列表中基类的排列顺序无关 include includeusingnamespacestd classTeacher public Teacher stringnam inta stringt name nam age a title t voiddisplay cout name name endl cout age age endl cout title title endl 36 protected stringname intage stringtitle classStudent public Student stringnam chars floatsco name1 nam sex s score sco voiddisplay1 cout name name1 endl cout sex sex endl cout score score endl 37 protected stringname1 charsex floatscore classGraduate publicTeacher publicStudent 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 38 private floatwage intmain Graduategrad1 Wang li 24 m assistant 89 5 1234 5 grad1 show return0 在两个基类中分别用name和name1来代表姓名 其实这是同一个人的名字 从Graduate类的构造函数中可以看到总参数表中的参数nam分别传递给两个基类的构造函数 作为基类构造函数的实参 39 解决这个问题有一个好方法 在两个基类中可以都使用同一个数据成员名name 而在show函数中引用数据成员时指明其作用域 如cout name Teacher name endl 这就是惟一的 不致引起二义性 能通过编译 正常运行 通过这个程序还可以发现一个问题 在多重继承时 从不同的基类中会继承一些重复的数据 如果有多个基类 问题会更突出 在设计派生类时要细致考虑其数据成员 尽量减少数据冗余 4 6 3多重继承引起的二义性问题 40 多重继承可以反映现实生活中的情况 能够有效地处理一些较复杂的问题 使编写程序具有灵活性 但是多重继承也引起了一些值得注意的问题 它增加了程序的复杂度 使程序的编写和维护变得相对困难 容易出错 其中最常见的问题就是继承的成员同名而产生的二义性 ambiguous 问题 a 多个基类中有同名成员 41 include includeusingnamespacestd classA protected inta public A inta this a a voiddisplay couta a voiddisplay cout B a a endl 42 classC publicA publicB private intb public C intAa intBa intb A Aa B Ba this b b voidshow A display B display cout C b b endl voidmain Cc 1 2 3 c display 二义性 c show c B display 43 所以类 中数据成员全名应该为下图所示 44 b 多个基类和派生类中都有同名成员 classC publicA publicB private inta public C intAa intBa intCa A Aa B Ba a Ca voiddisplay A display B display cout C a a endl 将前面的 类改为如下的形式 45 这时类 的成员构成如左图所示 在类中有三个a 三个display 函数 思考 如下的main函数能否执行 voidmain Cc 1 2 3 c display c A display c B display 这时c dispaly是可以执行的 原因是类 中提供的display函数隐藏了基类 和基类 中的display函数 所以直接访问display函数是在访问类 中新增加的成员函数display 46 C 如果类A和类B是从同一个基类派生的 47 前面提到派生类的对象也是基类的对象 因为派生类中继承了基类中的所有成员 除构造函数和析构函数 准确的说应该是 公有派生类的对象是基类的对象 因为只有公有派生类中成员的访问属性与基类完全相同 基类能实现的功能在公有派生类中一定能够实现 4 7基类与派生类的转换 基本数据类型在一定条件下可以进行类型转化 那么基类对象与派生类对象之间是不是也可以进行转化 由于公有派生类对象也是基类的对象 所以派生类对象可以自动转化为基类对象 表现在以下几方面 48 include includeusingnamespacestd classPerson public Person stringnam chars inta name nam sex s age a Person diplay cout 姓名 name endl cout 性别 sex endl cout 年龄 age endl protected stringname charsex intage 49 classTeacher publicPerson public Teacher stringnam chars inta stringt Person nam s a title t Teacher diplay Person diplay cout 职称 title protected stringtitle voidmain Teachert zhangSan m 25 assistant Personp t p diplay Personp zhangSan m 25 Teachert p t diplay 50 1 派生类的对象可以赋值给基类对象 voidmain Teachert zhangSan m 25 assistant Personp t p diplay 派生类的对象赋值给基类对象时舍弃了派生类自己增加的成员 只是将由基类继承而来的数据成员的值赋给基类对象的相应成员 基类的对象不能够赋值给派生类 如下代码错误Personp zhangSan m 25 Teachert p errort diplay 51 2 派生类的地址可以赋值给基类的指针 voidmain Teachert zhangSan m 25 assistant Personp Lis
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 建筑协议书汇编15篇
- 1+X幼儿照护(初级)知到智慧树答案
- 《红楼梦》“三书”浅说知到智慧树答案
- 《创新创业过程与方法》知到智慧树答案
- 零售行业变革与挑战
- (高级)审计理论与实务知到智慧树答案
- 机电设备抗震加固与设计方案
- 供水管网工程施工人员培训方案
- 燃气项目施工临时设施方案
- 水稻小麦课件
- 厨房消防安全培训
- 小陈 税务风险应对常见指标与答复思路
- 2025年《中华人民共和国档案法》知识培训试题及答案
- 2026年高考政治一轮复习:必修2《经济与社会》知识点背诵提纲
- 2025至2030年中国建筑膜行业市场调查研究及发展趋势预测报告
- 2025年急诊急救试题(附答案)
- 2025年北京市中考语文试卷(含答案与解析)
- (正式版)HGT 22820-2024 化工安全仪表系统工程设计规范
- 实验报告-探究杠杆的平衡条件
- 第3章access2010查询操作-上传
- 钳工手工制作六角螺母详细
评论
0/150
提交评论