C程序设计课件(第2章).ppt

上传人:max****ui 文档编号:8291143 上传时间:2020-03-28 格式:PPT 页数:47 大小:300KB
返回 下载 相关 举报
C程序设计课件(第2章).ppt_第1页
第1页 / 共47页
C程序设计课件(第2章).ppt_第2页
第2页 / 共47页
C程序设计课件(第2章).ppt_第3页
第3页 / 共47页
点击查看更多>>
资源描述
2020 3 28 1 第2章继承与派生 本章学习重点掌握内容 继承的概念 派生类的建立及继承的方式 各种继承方式下基类成员的访问机制 派生类的构造函数和析构函数 多重继承 虚基类 2020 3 28 2 第2章继承与派生 2 1继承与派生的基础知识2 2类的继承方式2 3派生类的构造函数与析构函数2 4基类与派生类的转换2 5多重继承2 6虚基类2 7综合应用实例 2020 3 28 3 2 1继承与派生的基础知识 2 1 1继承与派生的基本概念现实世界中 许多事物之间的并不是孤立存在的 它们存在共同的特性 有细微的差别 可以使用层次结构描述它们之间的关系 例如交通工具的层次结构如图2 1所示 2020 3 28 4 2 1 1继承与派生的基本概念 C 通过类派生 ClassDerivation 的机制支持继承 Inheritance 允许程序员在保持原有类特性的基础上进行扩展 增加功能 派生出新类 继承是面向对象程序设计中的代码复用的最重要的手段之一 被继承的类称为基类 BaseClass 父类或超类 Superclass 而新产生的类称为派生类 DerivedClass 或子类 Subclass 基类和派生类的集合称作类继承层次结构 Hierarchy 继承呈现了面向对象程序设计的层次结构 2020 3 28 5 2 1 1继承与派生的基本概念 一个新类从已有的类获得其已有的特性称为继承 通过继承 新类获得了父类的所有数据成员和成员函数 并可以添加自己的数据成员和成员函数 一个基类可以派生出很多的子类 一个子类也可以作为另一个新类的基类 因此基类和子类是相对而言的 继承的方式有以下2种 单一继承和多重继承 2020 3 28 6 2 1 1继承与派生的基本概念 单一继承和多重继承 请注意图中箭头的方向 本书约定 箭头表示继承的方向 由子类指向基类 2020 3 28 7 2 1 2派生类的定义 定义派生类的一般格式为 class派生类名 继承方式基类名 private 成员表1 派生类增加或重写的私有成员protected 成员表2 派生类增加或重写的保护成员public 成员表3 派生类增加或重写的公有成员 2020 3 28 8 2 1 2派生类的定义 其中 基类名是已声明的类 派生类名是新生成的类名 继承方式规定了如何访问从基类继承的成员 继承的方式包括 私有继承 private 保护继承 protected 公有继承 public 不同的继承方式下 派生类继承的父类成员的访问权限是不同的 继承方式可以省略不写 默认的继承方式为私有继承 private 派生类的定义中包括子类新增加的成员和继承父类需要重写的成员 新添加的成员是派生类对基类的发展 说明派生类新的属性和方法 派生类继承了父类的数据成员和成员函数 有时继承来的成员函数需要改进 以满足新类的实际需要 C 允许在派生类中重新声明和定义这些成员函数 使这些函数具有新的功能 称之为重写或覆盖 重写函数起屏蔽 更新作用 取代基类成员 完成新功能 2020 3 28 9 2 1 2派生类的定义 例2 1 已知盒子CBox类 用继承与非继承两种不同的方法定义彩色盒子CColorbox类 分析 盒子类 Cbox 具有长 宽和高 成员函数SetLength SetWidth 和SetHeight 分别设置盒子的长 宽和高 成员函数Volume 计算盒子的体积 彩色盒子除具有以上特性外 还有一个数据成员color表示盒子的颜色 相应的成员函数SetColor 用于设置彩色盒子的颜色 2020 3 28 10 2 1 2派生类的定义 非继承的方式 分别定义CBox类和CColorbox类盒子类的定义 代码见备注彩色盒子类的定义 代码见备注 2020 3 28 11 2 1 2派生类的定义 使用派生类定义 classCColorbox publicCBox 公有继承 新增的私有数据成员public voidSetColor intc 新增的成员函数 color c private intcolor 利用继承机制产生类比第一种简单多了 但功能一样 派生类CColorbox公有继承Cbox类 它包括基类CBox类的全部数据成员 length width height 和成员函数 SetWidth SetHeigh和SetWidth 但访问权限发生了变化 并且添加自己的新成员 数据成员color和成员函数SetColor 2020 3 28 12 2 1 3派生类的生成 仔细分析派生新类这个过程 实际是经历了以下步骤 首先继承基类的成员 不论是数据成员 还是成员函数 除构造函数与析构函数外全部接收 全部成为派生类的成员 第二步是重写基类成员 当基类成员在派生类的应用中不合适时 可以对继承的成员加以重写 如果派生类声明了一个与基类成员函数相同的成员函数时 派生类中的新成员则屏蔽了基类同名成员 类似函数中的局部变量屏蔽全局变量 称为同名覆盖 Override 第三步定义新成员 新成员必须与基类成员不同名 是派生类自己的新特性 派生类新成员的加入使得派生类在功能上有所发展 这一步是继承与派生的核心特征 第四步是重写构造函数与析构函数 因为派生类不继承基类的构造函数与析构函数 并且派生类的需要对新添加的数据成员进行必要的初始化 所以构造函数与析构函数需要重写 2020 3 28 13 2 2类的继承方式 派生类中包含基类的成员和派生类自己增加的成员 那么这两部分的成员关系和访问权限该如何确定呢 在继承机制中 并不是简单的把基类的私有成员直接作为派生类的私有成员 把基类的公有成员直接作为派生类的公有成员 派生类继承的基类成员访问权限由继承方式来控制 继承方式有三种 public 公有 继承 protected 保护 继承和private 私有 继承 不同的继承方式 决定了从基类继承来的成员的访问权限 下面分别介绍不同继承方式下 派生类成员的访问权限 2020 3 28 14 2 2 1公有继承 当定义一个派生类时 将基类前的继承方式指定为public 则称为公有派生 或公有继承 采用公有继承方式时 基类的公有成员和保护成员的访问权限在派生类中不变 而基类的私有成员在派生类中是不可访问 但它仍然是基类的私有成员 如果需要在派生类中引用继承基类的私有成员 那么需要通过基类的公有或保护的成员函数访问 例2 3 演示公有继承方式下 不同成员的访问权限 程序代码见备注 2020 3 28 15 2 2 1公有继承 例2 4 公有派生方式下如何访问继承的基类原有私有数据成员 程序代码见备注 2020 3 28 16 2 2 2私有继承 当定义一个派生类时 将基类前的继承方式指定为private 则称为私有继承 用私有继承方式建立的派生类称为私有派生类 其基类成为私有基类 采用私有继承方式时 私有基类的公有成员和保护成员在私有派生类中成为私有成员 即派生类成员可访问它们 而派生类外不可访问它们 基类的私有成员在派生类中成为不可访问的成员 私有继承基类成员的访问权限如表2 2所示 私有继承的意义是将基类中原来能被外部访问的成员隐藏起来 不让外界引用 例2 5 私有继承演示 2020 3 28 17 2 2 2私有继承 由上例可以看到私有继承方式 1 不能通过派生类对象 box1 引用从私有继承过来的任何成员 如box1 set 3 5 6 或box1 length 100 2 在派生类内部 如派生类的成员函数 不可以访问基类的私有成员 如length len length为基类的私有成员 但可以访问基类的公有和保护成员 如height h height为基类的保护成员 3 如果派生类需要访问基类的私有成员 可以通过派生类的成员函数调用基类的公有成员函数实现如 voidset 1 doublelen doublew doubleh intc set len w h 基类的公有成员函数 由上可以看出 私有派生的限制太多 一般不经常使用 2020 3 28 18 2 2 3保护继承 当定义一个派生类时 将基类前的继承方式指定为protected 则称为保护继承 在保护继承中 基类的公有成员和保护成员成为派生类的保护成员 在派生类中可以直接访问 但在派生类外不能直接访问任何基类成员的 基类中的私有成员成为派生类的不可访问成员 在派生类中不可直接访问 保护继承基类成员的访问权限如表2 3所示 保护继承的意义是将基类的公有成员也保护起来 不让类外部任意访问 2020 3 28 19 继承的方式有三种 使用不同继承方式 基类的成员在派生类中的访问权限也不同 不同继承方式下基类成员在派生类的访问权限总结如表2 4所示 2 2 4继承方式的总结和比较 2020 3 28 20 2 3派生类的构造函数与析构函数 派生类的成员是由基类中的数据成员和派生类中新增的数据成员共同构成 而在继承机制下 构造函数不能够被继承 因此 对继承过来的基类成员的初始化工作也得由派生类的构造函数完成 也就是说在定义派生类的构造函数时 既要初始化派生类新增数据 又要初始化基类的成员 所以 在定义派生类的构造函数时 有两步需要做 编写代码完成自己的数据成员进行初始化 调用基类构造函数使基类数据成员得以初始化 2020 3 28 21 2 3 1简单派生类的构造函数 单一继承的构造函数的定义形式为 派生类名 派生类构造函数名 参数总表 基类构造函数名 参数名表 派生类新增成员的初始化语句 定义派生类的构造函数时 在构造函数的参数总表中包括基类构造函数所需的参数和派生类新增的数据成员初始化所需的参数 冒号后面基类构造函数名 参数名表 表示要调用基类的构造函数 例2 6 演示派生类的构造函数执行顺序 2020 3 28 22 2 3 2析构函数 析构函数的功能是做善后工作 析构函数无返回类型也没有参数 情况比较简单 在派生过程中 基类的析构函数不能继承 如果需要析构函数的话 要在派生类中重新定义 派生类析构函数定义格式与非派生类无任何差异 只要在函数体内把派生类新增一般成员处理好就可以了 而对基类成员的善后工作 系统会自己调用基类的析构函数来完成 如果没有显示的定义析构函数 系统会自动生成一个默认的析构函数 清理工作就是靠它们来完成的 析构函数各部分执行次序与构造函数相反 首先对派生类新增成员析构 然后对基类成员析构 2020 3 28 23 2 3 3复杂派生类的构造函数和析构函数 一个派生类中新增加的成员可以是简单的数据成员 也可以是类对象 派生类可以是单一继承 也可以是多重继承 假如派生类是多重继承 并且新增数据成员有一个或多个类对象 那么派生类需要初始化的数据有三部分 继承的成员 新增类对象的成员和新增普通成员 这种复杂派生类的构造函数定义如下 派生类名 派生类构造函数名 总参数表 基类构造函数名1 参数表1 基类构造函数名2 参数表2 子对象名1 参数表n 子对象名2 数表n 1 派生类新增普通数据成员的初始化 2020 3 28 24 2 3 3复杂派生类的构造函数和析构函数 派生类构造函数的调用顺序如下 基类构造函数 按它们在派生类定义中的先后顺序 依次调用 子对象的构造函数 按它们在派生类定义中的先后顺序 依次调用 派生类的构造函数 复杂派生类的析构函数 只需要编写对新增普通成员的善后处理 而对类对象和基类的善后工作是由类对象和基类的析构函数完成的 析构函数的调用顺序与构造函数相反 例2 7 复杂继承举例 2020 3 28 25 2 3 3复杂派生类的构造函数和析构函数 例2 7 复杂继承举例 2020 3 28 26 2 3 3复杂派生类的构造函数和析构函数 在派生类构造函数使用中应注意以下问题 1 派生类构造函数的定义中可以省略对基类构造函数的调用 其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数 当然 基类中没有定义构造函数 派生类调用基类的缺省构造函数 2 当基类的构造函数使用一个或多个参数时 则派生类必须定义构造函数 提供将参数传递给基类构造函数途径 在有的情况下 派生类构造函数的函数体可能为空 仅起到参数传递作用 2020 3 28 27 2 4基类与派生类的转换 基类与派生类对象之间有赋值兼容关系 由于派生类中包含从基类继承的成员 因此可以将派生类的值赋给基类对象 在用到基类对象的时候可以用其子类对象代替 具体表现在以下几个方面 派生类对象可以向基类对象赋值可以用子类 即公用派生类 对象对其基类对象赋值 如Cbox与CColorbox CBoxbox 定义基类CBox对象boxCColorboxcolorbox 定义类Ccolorbox的对象colorboxbox colorbox 用派生类B对象b1对基类对象a1赋值在赋值时舍弃派生类自己的成员 实际上 所谓赋值只是对数据成员赋值 对成员函数不存在赋值问题 2020 3 28 28 2 4基类与派生类的转换 2 派生类对象可以向基类对象的引用进行赋值或初始化CBoxbox 定义基类CBox对象boxCColorboxcolorbox 定义派生类Ccolorbox的对象colorboxCBoxr2与colorbox具有相同的起始地址 2020 3 28 29 2 4基类与派生类的转换 3 派生类对象的地址可以赋给指向基类对象的指针变量 也就是说 指向基类对象的指针变量也可以指向派生类对象 例如 CBoxbox 定义基类CBox对象boxCColorboxcolorbox 定义类CBox的公用派生类Ccolorbox的对象colorboxCBox pt 调用colorbox SetHeight 函数 2020 3 28 30 2 5多重继承 前面主要介绍了单一继承 仅仅提到多重继承的概念 在现实世界中 很多时候一个类会有两个或两个以上的基类 例如沙发床 既继承了床的特性又继承了沙发的特性 沙发床的多重继承结构图如图2 7所示 C 中 定义派生类时 派生类有两个或多个基类称为多重继承 2020 3 28 31 2 5多重继承 2 5 1多重继承的定义多重继承可以看作是单一继承的扩展 多重继承的定义格式如下 class派生类名 继承方式1基类名1 继承方式2基类名 public 新增加的公有成员 protected 新增加的保护成员 private 新增加的私有成员 2020 3 28 32 2 5多重继承 多重继承派生类的构造函数格式如下 派生类名 派生类构造函数名 总参数表 基类名1 参数表1 基类名2 参数表2 派生类构造函数体 其中 总参数表中各个参数包含了其后基类的各个分参数表 多重继承下派生类的构造函数与单一继承下派生类构造函数相似 它必须同时负责该派生类所有基类构造函数的调用 同时 派生类的参数必须包含完成所有基类初始化所需的参数 2020 3 28 33 2 5 1多重继承的定义 例2 8 设计沙发床类 分析 床类可以用来睡觉Sleep 沙发类可以用来看电视WatchTV 沙发床具有床和沙发两者的特特性 沙发床还有自己的特性 折叠FoldOut 因此先定义床类和沙发类 沙发床类由这两个类派生 然后添加沙发床自己的特性 程序代码如下 2020 3 28 34 2 5 1多重继承的定义 沙发床类继承了床类和沙发类 具有了沙发和床的特性 因此可以使用派生类对象ss调用床的Sleep 成员函数 完成睡觉功能 通过调用沙发WatchTV 成员函数完成看电视功能 调用新增的FoldOut 可以完成折叠与打开功能 派生类对象调用构造函数的顺序是先基类的构造函数再派生类的构造函数 有两个基类 Cbed和Csofa 时 基类构造函数调用的顺序是按照声明派生类时基类的排列顺序来进行 2020 3 28 35 2 5 2多重继承中的二义性问题 多重继承反映了现实生活中的情况 使一些复杂的问题简单化 提高了程序开发效率 但多重继承中存在两类二义性问题 1 调用不同基类中相同成员时产生的二义性派生类的多个基类之间出现相同的成员 则在派生类中访问此成员时会出现二义性 2020 3 28 36 2 5 2多重继承中的二义性问题 解决多重继承中调用不同基类中相同成员时产生的二义性可以使用以下方法 1 使用域作用符解决二义性问题可以使用 域作用符对此成员函数加以区分 ob1 CBed SetWeight 100 调用基类Cbed的函数成员SetWeight ob1 CSofa SetWeight 200 调用基类CSofa的函数成员SetWeight 使用域作用符可以消除编译时二义性 程序员需要知道类的继承层次信息 加大程序开发的复杂度 2020 3 28 37 2 5 2多重继承中的二义性问题 2 覆盖函数同名隐藏 覆盖 派生类继承了两个基类的print 函数 派生类在自己的构造函数中又重写了print 函数 如果在主函数如下 voidmain CSleeperSofaob1 继承了两个基类的特性ob1 print 正确 print 是覆盖函数不存在二义性 ob1 print 语句能通过编译 这是因为派生类新增的成员函数print 覆盖了基类中的同名成员 这点与局部变量屏蔽全局变量类似 可以使用域作用符调用基类的成员函数 如ob1 CBed print 则调用基类Cbed的print 2020 3 28 38 2 5 2多重继承中的二义性问题 注意 在不是虚函数的情况下 如果派生类中新增的成员函数与基类的某一成员函数同名 则该函数会隐藏基类中所有该函数的重载函数 includeclassCA public voidf intx cout thef int ofCA endl voidf cout thef ofCA endl classCB publicCA public voidf cout thef ofCB endl voidmain CBb b f b f 0 2020 3 28 39 2 5 2多重继承中的二义性问题 2 派生类中访问公共基类成员时产生的二义性派生类有多个基类 而这多个基类又从同一个基类派生 则在派生类中访问公共基类成员时会出现二义性 此时引入虚基类解决 2020 3 28 40 2 5 2多重继承中的二义性问题 例2 9 一个公共基类在派生类中产生的二义性问题 类B与类C由类A公有派生 而类D由类B与类C公有派生 如图2 9所示 则类D中将包含类A的两个拷贝 这种同一个基类在派生类中产生多个拷贝不仅多占用了存储空间 而且可能会造成二义性问题 2020 3 28 41 2 6虚基类 在多重继承中 虚基类保证从不同路径继承过来的公共基类成员在内存中就只有一个拷贝 所以可解决同名基类成员产生的二义性问题 2 6 1虚基类的定义虚基类的定义是在派生类的定义过程中定义的 语法格式如下 class派生类名 virtual继承方式基类类名 其中 virtual关键字只对紧随其后的基类名起作用 上述定义使得基类为派生类的虚基类 定义了虚基类之后 虚基类的成员在派生过程中和派生类一起维护同一个内存拷贝 例2 10 用虚基类的方法解决公共基类成员产生的二义性 2020 3 28 42 2 6虚基类 1 采用虚基类后 在D类中只有唯一的数据成员x 所以在建立D类的对象后 调用Print 输出x时 不产生二义性 2 A类为虚基类以后 公共基类A的构造函数仅被执行一次 因为具有虚基类的派生类构造函数与一般派生类构造函数有所不同 在例2 10中 D的构造函数中增加一个给虚基类的初始化列表项A a 而给两个直接基类B C初始化列表项B a b C d e 仍然保留 C 为了保证虚基类构造函数仅被执行一次 规定在创建对象的派生类构造函数中优先调用虚基类的构造函数 并在执行后 忽略直接基类初始化列表对虚基类的构造函数的调用 保证虚基类的构造函数仅被执行一次 2020 3 28 43 2 6虚基类 2 6 2虚基类及其派生类构造函数执行顺序在同时具有虚基类和非虚基类的的派生类对象的创建中 首先是虚基类的构造函数被调用 并按它们声明的顺序调用 接着是非虚基类的构造函数按它们声明的顺序调用 其次是对象成员的构造函数 最后是派生类自己的构造函数被调用 例2 12 在采用虚基类的多重继承中 分析构造函数与析构函数的执行顺序 2020 3 28 44 2 7综合应用实例 实例一 编写一个学生信息和教师信息输入和显示的管理程序 学生信息有编号 姓名 性别 生日和班级名 各门课程的成绩 教师信息有编号 姓名 性别 生日和职称 部门 要求将学生和教师信息的共同特性设计成一个类 CPerson类 作为学生CStudent类和教师CTeacher类的基类 分析 根据题目要求 可以将编号 姓名 性别 生日作为基类CPerson类的数据成员 为实现输入和显示这些信息 在基类CPerson类设计Input 和PrintPersonInfo 这两个成员函数实现 在派生类CStudent类和CTeacher类中主要考虑自有信息输入和显示即可 这三个类的类图如图2 12所示 2020 3 28 45 2 7综合应用实例 实例一 编写一个学生信息和教师信息输入和显示的管理程序 2020 3 28 46 2 7综合应用实例 实例二 编写程序 实现某个公司的工资管理 该公司主要有四类人员 经理 技术员 销售员和销售经理 要求存储这些人员的编号 姓名 并且能计算各类人员月工资和显示该员工全部信息 他们的月收入如下计算 经理 月工资固定8500 技术员 每小时150元计算 销售员 按当月销售额的7 提取 销售经理 每月固定4000 另按其部门每月的销售额的0 5 提成 分析 依题意设计一个基类Employee 其中有3个数据成员no 编号 name 姓名 salary 月工资 和成员函数pay 计算月工资 display 显示月工资 及构造函数 由基类Employee派生出经理 技术员 销售员 销售经理这4个类 建立各个类层次结构如图2 13所示 2020 3 28 47 2 7综合应用实例 实例二 编写程序 实现某个公司的工资管理 该公司主要有四类人员 经理 技术员 销售员和销售经理 要求存储这些人员的编号 姓名 并且能计算各类人员月工资和显示该员工全部信息
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!