C++程序设计_04章多态性_第1页
C++程序设计_04章多态性_第2页
C++程序设计_04章多态性_第3页
C++程序设计_04章多态性_第4页
C++程序设计_04章多态性_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

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

文档简介

第4章多态性 4 1多态性概述多态性的概念 polymorphisn 是指多种表现形式 具体地说 就是把同样的消息发给不同类型的对象后可能导致完全不同的行为 即 一个对外接口 对应多个内在实现方法 多态性连同前面所讲过的封装性和继承性一起 构成了面向对象程序设计的三大基本特征 多态性的实现 函数重载运算符重载虚函数从系统实现角度来看 多态性包括静态多态性 编译时多态性 和动态多态性 运行时多态性 两大类 静态多态性 是通过函数重载和类属机制来实现的 在程序编译时系统就能确定调用哪个函数 因此静态函数又称编译时的多态性 例如 函数重载和运算符重载就属于这种情况 动态多态性 是通过虚函数 继承机制 以及动态绑定机制来实现的 在程序运行过程中才动态地确定操作指针所指的对象 主要通过虚函数和重写来实现 4 2运算符重载 4 2 1运算符重载的概念运算符重载 是指将同一个运算符作用于不同的运算对象时 可以实现不同的操作 其意义在于 使程序的表达方式更加符合人们平时的表达习惯 易于接受 运算符具有函数的特性 我们可以将一个运算符看成是一个函数名 我们使用一个运算符 就相当于是在调用一个函数 比如 3 2可以理解成 3 2 即调用函数 并给出两个实参3和2 为此 运算符重载和函数重载本质上就是相同的 4 2 2运算符重载的规则1 有5种运算符不能重载 它们是类属关系运算符 成员指针运算符 作用域分辨符 sizeof运算符和条件运算符 2 重载后的运算符有四个 不能改变 不能改变运算符原有的优先级 不能改变运算符原有的结合性 不能改变运算符原有的语法结构 不能改变运算符操作数的个数 3 至少要有一个操作对象是自定义类型 否则就不必重载了 4 2 3运算符重载的语法返回类型类名 operator操作符 形参表 4 2 4运算符重载的方法1 用类成员函数实现运算符重载 其中 等运算符必须采用这种方法实现重载 用类成员函数实现重载时 参数个数 原操作数个数 1 后置 除外 比如 要重载一个双目运算符P为类成员函数 使之能够实现表达式oprd1Poprd2的运算 其中假定oprd1为A类的对象 则P就应该被重载为A类的成员函数 形参类型应该是oprd2所属的类型 经重载后 执行表达式oprd1Poprd2就相当于是执行oprd1 operatorP oprd2 又如 要重载前置单目运算符P为类成员函数 使之能够实现表达式Poprd的运算 其中假定oprd为A类的对象 则P就应该被重载为A类的成员函数 没有形参 经重载后 执行表达式Poprd就相当于执行oprd operatorU 再如 要重载后置单目运算符P 或 为类成员函数 使之能够实现表达式oprdP 其中假定oprd为A类的对象 则P就应该被重载为A类的成员函数 且具有一个int类型的形参 经重载后 执行表达式oprdP就相当于执行oprd operatorP 0 这里的参数个数与重载前置单目运算符时不相同 以此区分前置的P 4 2 5运算符重载的例子 例1请用类成员函数重载 两个双目运算符 使之可以用于复数的运算 includeusingnamespacestd 可用 include来替换这两行classcomplex 复数类声明 public 外部接口complex doubler 0 0 doublei 0 0 real r imag i complexoperator complexc2 重载为成员函数complexoperator complexc2 重载为成员函数voiddisplay 输出复数private 私有数据成员 自己内部使用doublereal 复数实部doubleimag 复数虚部 complexcomplex operator complexc2 complexc c real c2 real real c imag c2 imag imag returncomplex c real c imag complexcomplex operator complexc2 complexc c real real c2 real c imag imag c2 imag returncomplex c real c imag voidcomplex display cout real imag i endl voidmain complexc1 2 3 c2 6 8 c3 声明复数类的对象cout c1 c1 display cout c2 c2 display c3 c1 c2 使用重载运算符完成复数减法cout c3 c1 c2 c3 display c3 c1 c2 使用重载运算符完成复数加法cout c3 c1 c2 c3 display 运行结果 c1 2 3 ic2 6 8 ic3 c1 c2 4 5 ic3 c1 c2 8 11 i 例2请用类成员函数分别重载单目运算符 的前置和后置用法 使之可以用于时间的运算 不断地增加1秒 includeusingnamespacestd classClock 定义一个时钟类 public Clock intNewH 0 intNewM 0 intNewS 0 voidShowTime Clock Clock 将这个被前置运算后的对象通过引用返回 ClockClock operator int 后置单目运算符重载函数 注意形参表中的这个int参数 可以不写形参名 写了也不用 Clockold this this 调用前置单目运算符重载函数returnold 其它成员函数的实现Clock Clock intNewH intNewM intNewS Hour NewH Minute NewM Second NewS voidClock ShowTime cout Hour Minute Second endl voidmain ClockmyClock 23 59 59 cout Time myClock ShowTime cout Show Time myClock ShowTime cout ShowTime myClock ShowTime 运行结果 Time 23 59 59Show Time 0 0 0ShowTime 0 0 0 2 用友元函数实现运算符重载 友元概念的引入 按照封装性的概念 一个类之外的函数只能访问这个类中的public成员 如果要让这个类之外的函数访问到自己的private或protected成员 就必须打破原有的封装性 方法就是设定该类的友元 应该说 友元和封装是一对相反的概念 一个是要 实现开放 另一个则是要 限制开放 一个类的友元可以是一个普通的函数 即非成员函数 另一个类的成员函数或者另一个完整的类 如何设置一个类的友元呢 在类的定义中使用friend保留字进行说明 并在friend之后列出友元的名字 如果是把一个完整的类B作为类A的友元 则类B中所有的成员函数都将被视为类A的友元函数 用友元函数实现重载时 参数个数 原操作数个数 且至少应该有一个自定义类型的形参 双目运算符P重载后 表达式oprd1Poprd2等同于operatorP oprd1 oprd2 前置单目运算符P重载后 表达式Poprd等同于operatorP oprd 后置单目运算符P重载后 表达式oprdP等同于operatorP oprd 0 例3请用友元函数重载 两个双目运算符 使之可以用于复数的运算 includeusingnamespacestd classcomplex public complex doubler 0 0 doublei 0 0 real r imag i friendcomplexoperator complexc1 complexc2 运算符 被重载为本类的友元函数friendcomplexoperator complexc1 complexc2 运算符 被重载为本类的友元函数voiddisplay 显示复数的值private 私有数据成员doublereal doubleimag complexoperator complexc1 complexc2 友元函数实现运算符重载 returncomplex c2 real c1 real c2 imag c1 imag complexoperator complexc1 complexc2 友元函数实现运算符重载 returncomplex c1 real c2 real c1 imag c2 imag 其他函数和主函数同例1注意 这里的operator 和operator 是两个普通函数 非成员函数 两种重载方法的比较 一般说来 单目运算符最好被重载为成员函数 对双目运算符最好被重载为友元函数 双目运算符重载为友元函数比重载为成员函数更方便 但是 有的双目运算符还是重载为成员函数为好 例如 赋值运算符 因为 它如果被重载为友元函数 将会出现与赋值语义不一致的地方 4 3用虚函数实现多态性 4 3 1绑定方式绑定 binding 是指对于具有多种解释的名字 将名字与它的某个含义相关联的过程 对于函数而言 就是将函数调用与某个函数体对应起来 根据进行关联的时机不同 可将绑定分为早期绑定 又称静态绑定 和晚期绑定 又称动态绑定 静态绑定 绑定过程出现在编译阶段 在编译时就用对象名或者类名来限定要调用的函数 动态绑定 绑定过程出现在运行阶段 在程序运行时才确定将要调用的函数 在C 中 函数调用的默认绑定方式是静态绑定 只有通过基类类型的引用或指针调用被指定为虚函数的成员函数时才进行动态绑定 实现运行时多态性 为此 可以看出 运行时多态性是通过虚函数 继承机制 以及动态绑定机制来实现的 要实现运行时多态性必须同时满足下面4个条件 要有一个继承层次 在基类中要定义虚函数 在公有派生类中要对基类中定义的虚函数进行重定义 要通过基类指针 或基类引用 来调用虚函数 4 3 2虚函数虚函数 是指在类定义体中使用保留字virtual来声明的成员函数 这个含有虚函数的类 称为多态类 注意 1 virtual只能用在类的声明中 函数原型之前 不能用在函数实现时 2 派生类对基类中的虚函数进行重定义 既不是要实现重载也不是要实现隐藏 而是要实现覆盖 例4一个静态绑定的例子 includeusingnamespacestd classPoint public Point doublei doublej x i y j doubleArea const return0 0 private doublex y classRectangle publicPoint public Rectangle doublei doublej doublek doublel doubleArea const returnw h private doublew h Rectangle Rectangle doublei doublej doublek doublel Point i j w k h l voidfun Point 派生类的对象 运行结果 Area 0 例5一个动态绑定的例子 includeusingnamespacestd classPoint public Point doublei doublej x i y j virtualdoubleArea const return0 0 private doublex y classRectangle publicPoint public Rectangle doublei doublej doublek doublel virtualdoubleArea const returnw h private doublew h Rectangle Rectangle doublei doublej doublek doublel Point i j w k h l voidfun Point 派生类的对象 运行结果 Area 375 3 virtual具有继承性 一旦基类中声明了虚函数 无论派生类中是否再声明 同原型的函数都自动成为虚函数 例6 includeusingnamespacestd classB0 基类B0声明 public virtualvoiddisplay 虚成员函数 cout B0 display endl classB1 publicB0 公有派生 public voiddisplay cout B1 display endl classD1 publicB1 公有派生 public voiddisplay cout D1 display endl voidfun B0 ptr 普通函数 基类指针 ptr display intmain B0b0 p 声明基类指针p和对象b0B1b1 声明派生类对象D1d1 声明派生类对象p 调用派生类D1函数成员 运行结果 B0 display B1 display D1 display 4 什么时候需要虚析构函数如果某个类不包含虚函数时 那这个类一般不要用作基类 为此 当已知一个类不会用作基类时 其析构函数最好不要设置为虚析构函数 因为它会为这个类增加一个虚函数表 使得对象的体积翻倍 还有可能降低其可移植性 所以基本的一条是 无故的声明虚析构函数和永远不去声明一样都是错的 实际上 很多人这样总结 当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数 虚析构函数是为了解决这样的一个问题 基类的指针指向派生类对象 并用基类的指针删除派生类对象 例7使用虚析构函数的例子 includeclassabstract base public virtual abstract base cout abstract base abstract base iscalled endl 说明 例子中 由于b是基类的对象 则在deleteb时一般情况下应该去调用基类的析构函数 但这样的话 派生类的析构函数就得不到调用 所以要打破原来的这种析构顺序 就必须把基类的析构函数设置为虚拟的 这样在deleteb时才会先调用派生类的析构函数 再调用基类的析构函数 classconcrete derived publicabstract base public concrete derived cout concrete derived concrete derived iscalled endl voidmain concrete derived

温馨提示

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

评论

0/150

提交评论