c语言程序设计(郑莉第四版).ppt_第1页
c语言程序设计(郑莉第四版).ppt_第2页
c语言程序设计(郑莉第四版).ppt_第3页
c语言程序设计(郑莉第四版).ppt_第4页
c语言程序设计(郑莉第四版).ppt_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

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

文档简介

1 第八章 多态性 清华大学 郑 莉 C+语言程序设计 C+语言程序设计清华大学 郑莉 2 本章主要内容 l多态性 l运算符重载 l虚函数 l纯虚函数 l抽象类 l深度探索 C+语言程序设计清华大学 郑莉 3 多态性的概念 l多态性是面向对象程序设计的重要特 征之一。 l多态性是指发出同样的消息被不同类 型的对象接收时有可能导致完全不同 的行为。 l多态的实现: 函数重载 运算符重载 虚函数 C+语言程序设计清华大学 郑莉 4 重载函数的声明 l函数重载:两个以上的具有相同函数名的函数 ,但是形参的个数不同或类型不同,编译程序 将根据实参和形参的类型及个数的最佳匹配来 选择调用哪一个函数。 lC+允许功能相近的函数在相同的作用域内以相同函数 名声明,从而形成重载。方便使用,便于记忆。 l例: 形参类型不同 int add(int x, int y); float add(float x, float y); 形参个数不同 int add(int x, int y); int add(int x, int y, int z); 函 数 重 载 C+语言程序设计清华大学 郑莉 5 注意事项 int add(int x,int y); int add(int a,int b); 编译器不以形参名来区分 int add(int x,int y); void add(int x,int y); 编译器不以返回值来区分 函 数 重 载 重载函数的形参必须不同:个数不同或类型不同。 编译程序将根据实参和形参的类型及个数的最佳匹配来 选择调用哪一个函数。 C+语言程序设计清华大学 郑莉 6 问题举例复数的运算 class Complex /复数类声明 public: Complex(double r = 0.0,double i = 0.0) real = r; imag=i; void display() const;/显示复数的值 private: double real; double imag; ; 运算符重载 C+语言程序设计清华大学 郑莉 7 问题举例复数的运算 l用“+”、“-”能够实现复数的加减运算 吗? l实现复数加减运算的方法 重载“+”、“-”运算符 运算符重载 C+语言程序设计清华大学 郑莉 8 运算符重载的实质 l运算符重载是对已有的运算符赋予多重含义 l必要性 C+中预定义的运算符其运算对象只能是基本数 据类型,而不适用于用户自定义类型(如类) 。 l实现机制 运算符重载的实质是函数重载。 将指定的运算表达式转化为对运算符函数的调 用,运算对象转化为运算符函数的实参,根据 实参的类型来确定需要调用的函数。 编译系统对重载运算符的选择,遵循函数重载 的选择原则。 l主要优点 可以改变现有运算符的操作方式,用于自定义 的类类型使得程序看起来更加直观。 运算符重载 C+语言程序设计清华大学 郑莉 9 运算符重载 规则和限制 l可以重载C+中除下列运算符外的所有运算 符: 类运算符.、成员指针运算符 .* 、作用域运 算符 :和三目运算符 ?: l只能重载C+语言中已有的运算符,不可臆 造新的。 l不改变原运算符的优先级和结合性。 l不能改变操作数个数。 l经重载的运算符,其操作数中至少应该有一 个是自定义类型。 C+语言程序设计清华大学 郑莉 10 两种形式 l重载为类的非静态成员函数 l重载为非成员函数 运算符重载 C+语言程序设计清华大学 郑莉 11 运算符函数 l声明形式 函数类型 operator 运算符(形参) l重载为类成员函数时 参数个数=原操作数个数-1 (后置+、-除外 ) l重载为非成员函数时 参数个数=原操作数个数 ,且至少应该有一个自定义类型的形参。 运算符重载 C+语言程序设计清华大学 郑莉 12 运算符成员函数的设计 l双目运算符 B 如果要重载 B 为类成员函数,使之能够实 现表达式 oprd1 B oprd2,其中 oprd1 为 A 类对象,则 B 应被重载为 A 类的成员函 数,形参类型应该是 oprd2 所属的类型。 经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2) 运算符重载 C+语言程序设计清华大学 郑莉 13 运算符重载 例 8-1 将“+”、“-”运算重载为复数类 的成员函数。 l 规则: 实部和虚部分别相加减。 l 操作数: 两个操作数都是复数类的对象。 #include using namespace std; class Complex /复数类定义 public:/外部接口 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) /构造函数 Complex operator + (const Complex /运算符+重载成员函数 Complex operator - (const Complex /运算符-重载成员函数 void display() const;/输出复数 private:/私有数据成员 double real;/复数实部 double imag;/复数虚部 ; 14 Complex Complex:operator + (const Complex /创建一个临时无名对象作为返回值 Complex Complex:operator - (const Complex /创建一个临时无名对象作为返回值 15 void Complex:display() const cout using namespace std; class Clock /时钟类声明定义 public:/外部接口 Clock(int hour = 0, int minute = 0, int second = 0); void showTime() const; Clock /前置单目运算符重 载 Clock operator + (int);/后置单目运算符 重载 private:/私有数据成员 int hour, minute, second; ; 21 /构造函数 Clock:Clock(int hour, int minute, int second) l lif(0 hour=hour; l this-minute=minute; l this-second=second; l lelse l cout= 60) second -= 60; minute+; if (minute = 60) minute -= 60; hour = (hour + 1) % 24; return *this; 23 /后置单目运算符重载 Clock Clock:operator + (int) /注意形参表中的整型参数 Clock old = *this; +(*this);/调用前置“+”运算符 return old; 24 /其它成员函数的实现略 int main() Clock myClock(23, 59, 59); cout using namespace std; class Complex /复数类定义 public:/外部接口 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) /构造函数 friend Complex operator + (const Complex /运算符+重载 friend Complex operator - (Complex c1, Complex c2);/运算符-重载 friend ostream class Point public: Point(double x, double y) : x(x), y(y) double area() const return 0.0; private: double x, y; ; class Rectangle: public Point public: Rectangle(double x, double y, double w, double h); double area() const return w * h; private: double w, h; ; 静态绑定例 33 Rectangle:Rectangle(double x, double y, double w, double h) :Point(x, y), w(w), h(h) void fun(const Point class Point public: Point(double x, double y) : x(x), y(y) virtual double area() const return 0.0; private: double x, y; ; class Rectangle:public Point public: Rectangle(double x, double y, double w, double h); virtual double area() const return w * h; private: double w, h; ; /其他函数同上例 动态绑定例 35 void fun(const Point class Base1 /基类Base1定义 public: virtual void display() const;/虚函数 ; void Base1:display() const cout display();/“对象指针-成员名“ 39 int main() /主函数 Base1 base1;/定义Base1类对象 Base2 base2;/定义Base2类对象 Derived derived;/定义Derived类对象 fun(/用Base1对象的指针调用fun函数 fun(/用Base2对象的指针调用fun函数 fun( /用Derived对象的指针调用fun函数 return 0; 运行结果: Base1:display() Base2:display() Derived:display() 40 C+语言程序设计清华大学 郑莉 41 虚析构函数 为什么需要虚析构函数? l可能通过基类指针删除派生类对象; l如果你打算允许其他人通过基类指针 调用对象的析构函数(通过delete这 样做是正常的),就需要让基类的析 构函数成为虚函数,否则执行delete 的结果是不确定的。 虚 函 数 C+语言程序设计清华大学 郑莉 42 抽象类 带有纯虚函数的类称为抽象类: class 类名 virtual 类型 函数名(参数表)=0; /纯虚函数 . 纯虚函数与抽象类 C+语言程序设计清华大学 郑莉 43 抽象类 纯虚函数与抽象类 l作用 抽象类为抽象和设计的目的而声明,将有关的数 据和行为组织在一个继承层次结构中,保证派生 类具有要求的行为。 对于暂时无法实现的函数,可以声明为纯虚函数 ,留给派生类去实现。 l注意 抽象类只能作为基类来使用。 不能声明抽象类的对象。 构造函数不能是虚函数,析构函数可以是虚函数 。 C+语言程序设计清华大学 郑莉 44 例 8-5 纯虚函数与抽象类 #include using namespace std; class Base1 /基类Base1定义 public: virtual void display() const = 0;/纯虚函数 ; class Base2: public Base1 /公有派生类Base2定义 public: void display() const /覆盖基类的虚函数 cout display();/“对象指针-成员名“ int main() /主函数 Base2 base2; /定义Base2类对象 Derived derived;/定义Derived类对象 fun( /用Base2对象的指针调用fun函数 fun(/用Derived对象的指针调用fun函 数 return 0; 运行结果: Base2:display() Derived:display( ) 45 C+语言程序设计清华大学 郑莉 46 类型兼容规则 l一个公有派生类的对象在使用上可以 被当作基类的对象,反之则禁止。具 体表现在: 派生类的对象可以隐含转换为基类对象 。 派生类的对象可以初始化基类的引用。 派生类的指针可以隐含转换为基类的指 针。 l通过基类对象名、指针只能使用从基 类继承的成员 类型兼容 C+语言程序设计清华大学 郑莉 47 例7-4 类型兼容规则举例 #include using namespace std; class Base1 /基类Base1定义 public: void display() const cout display(); /“对象指针-成员名“ 48 C+语言程序设计清华大学 郑莉 int main() /主函数 Base1 base1;/声明Base1类对象 Base2 base2;/声明Base2类对象 Derived derived;/声明Derived类对象 /用Base1对象的指针调用fun函数 fun( /用Base2对象的指针调用fun函数 fun( /用Derived对象的指针调用fun函数 fun( return 0; 运行结果: Base1:display() Base1:display() Base1:display() 49 C+语言程序设计清华大学 郑莉 多态类型与非多态类型 l多态类型与非多态类型 有虚函数的类类型称为多态类型 其它类型皆为非多态类型 l二者的差异 语言层面的差异 l多态类型支持运行时类型识别 l多态类型对象占用额外的空间 设计原则上的差异 50 深 度 探 索 C+语言程序设计清华大学 郑莉 设计原则 l多态类型 多态类型的析构函数一般应为虚函数 l非多态类型 非多态类型不宜作为公共基类 l由于没有利用动态多态性,一般可以用组合,而 无需用共有继承; l如果继承,则由于析构函数不是虚函数,删除对 象时所执行操作与指针类型有关,易引起混乱。 把不需被继承的类型设定为非多态类型 l由于成员函数都是静态绑定,调用速度较快; l对象占用空间较小。 51 深 度 探 索 C+语言程序设计清华大学 郑莉 运行时类型识别 l运行时类型识别 允许在运行时通过基类指针(或引用)辨别 对象所属的具体派生类; 只对多态类型适用; 比虚函数动态绑定的开销更大,因此应仅对 虚函数无法解决的问题使用。 l运行时类型识别的方式 用dynamic_cast做类型转换的尝试; 用typeid直接获取类型信息。 52 深 度 探 索 C+语言程序设计清华大学 郑莉 dynamic_cast的使用 l语法形式 dynamic_cast(表达式) l功能 将基类指针转换为派生类指针,将基类引用 转换为派生类引用; 转换是有条件的 l如果指针(或引用)所指对象的实际类型与转换 的目的类型兼容,则转换成功进行; l否则如执行的是指针类型的转换,则得到空指针 ;如执行的是引用类型的转换,则抛出异常。 53 深 度 探 索 C+语言程序设计清华大学 郑莉 例8-9 dynamic_cast示例 #include using namespace std; class Base public: virtual void fun1() cout fun1(); /尝试将b转换为Derived1指针 Derived1 *d = dynamic_cast(b); /判断转换是否成功 if (d != 0) d-fun2(); int main() Base b; fun( Derived1 d1; fun( Derived2 d2; fun( return 0; 运行结果: Base:fun1() Derived1:fun1() Derived1:fun2() Derived2:fun1() Derived2:fun2() 55 C+语言程序设计清华大学 郑莉 typeid的使用 l语法形式 typeid ( 表达式 ) typeid ( 类型说明符 ) l功能 获得表达式或类型说明符的类型信息 l表达式有多态类型时,会被求值,并得到动态类型信息 ; l否则,表达式不被求值,只能得到静态的类型信息。 类型信息用type_info对象表示 ltype_info是typeinfo头文件中声明的类; ltypeid的结果是type_info类型的常引用; l可以用type_info的重载的“=”、“!=”操作符比较两类型 的异同; ltype_info的name成员函数返回类型名称,类型为 const char *。 56 深 度 探 索 C+语言程序设计清华大学 郑莉 例8-10 typeid示例 #include #include using namespace std; class Base public: virtual Base() ; class Derived: public Base ; 57 深 度 探 索 void fun(Base *b) /得到表示b和*b类型信息的对象 const type_info const type_info cout “typeid(b): “ () endl; cout “typeid(*b): “ () endl; if (info2 = typeid(Base) /判断*b是否为Base类型 cout “A base class!“ endl; int main() Base b; fun( Derived d; fun( return 0; 运行结果: typeid(b): class Base * typeid(*b): class Base A base class! typeid(b): class Base * typeid(*b): class Derived 58 C+语言程序设计清

温馨提示

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

评论

0/150

提交评论