《c程序设计基础》第九章-多态性.ppt
《《c程序设计基础》第九章-多态性.ppt》由会员分享,可在线阅读,更多相关《《c程序设计基础》第九章-多态性.ppt(132页珍藏版)》请在三一办公上搜索。
1、C+程序设计基础,第9章 多态性北京邮电大学信通院方莉,2,多态性(Polymorphism)是面向对象程序设计的主要特征之一。多态性对于软件功能的扩展和软件重用都有重要的作用。是学习面向对象程序设计必须要掌握的主要内容之一。本章主要内容多态性的基本概念运行多态的实现,虚函数模板,第9章 多态性,3,第9章 多态性,4,9.1.1 面向对象程序设计中的多态,多态性是指同样的消息被不同类型的对象接收时导致完全不同的行为。消息:指示要调用类的某个成员函数。行为:成员函数执行的结果被视为对象的行为。,5,面向对象程序设计中多态性表现为以下几种形式:重载多态:通过调用相同名字的函数,表现出不同的行为。
2、运算符重载是一种重载多态。运行多态:通过基类的指针,调用不同派生类的同名函数(虚函数),表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。模板多态,也称为参数多态:通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。,9.1.1 面向对象程序设计中的多态,6,一个具有多态性的程序语句,在执行的时候,必须确定究竟是调用哪一个函数。确定具有多态性的语句究竟调用哪个函数的过程称为联编(Binding),有的资料也翻译成“绑定”。联编有两种方式:静态联编动态联编,9.1.2 多态的实现:联编,7,在源程序编译的时候就能确定具有多态性的语句调用哪个函
3、数,称为静态联编。对重载函数的调用就是在编译的时候确定具体调用哪个函数,所以是属于静态联编。模板在编译时确定被调函数,属于静态联编。,静态联编,8,动态联编则是在程序运行时,才能够确定具有多态性的语句究竟调用哪个函数。用动态联编实现的多态,也称为运行时的多态。虚函数是支持运行多态的基础。,动态联编,9,第9章 多态性,10,9.2.1 重载和静态联编,以下是一个重载函数的例子。int add(int a)return a+10;int add(int a,int b)return a+b;int main()int x=1,y=2;add(x);add(x,y);return 0;,11,9.
4、2.2 覆盖与静态联编,覆盖(Override)现象只能出现在继承树中。在派生类中定义和基类中同名的成员函数,是对基类进行改造,为派生类增加新的行为的一种常用的方法。在某些情况下,覆盖会导致静态联编;另外一些情况下,覆盖会导致动态联编。,12,9.2.2 覆盖与静态联编,通过派生类对象调用同名成员函数通过基类指针调用同名成员函数,13,一.通过派生类对象调用同名成员函数,在派生类中可以定义和基类的成员函数同名的成员函数。这是对基类进行改造,是派生类增加新的行为的一种常用的方法。在程序编译的时候,就可以确定派生类对象具体调用哪个同名的成员函数。这是通过静态联编实现的多态。,14,例 9-1 用派
5、生类对象访问继承树中的同名函数/以下代码仅为示例,无法编译class B public:f();/基类class P:public B public:f();/派生类1class Q:public B public:f();/派生类2void main()P p;/创建派生类P的对象 Q q;/创建派生类Q的对象p.f();/调用P:f()q.f();/调用Q:f(),一.通过派生类对象调用同名成员函数,15,例1:定义Circle类和Rectangle类为Shape类的派生类,通过Circle类和Rectangle类的对象调用重载函数getArea()显示对象的面积。/例1:shape.hc
6、lass Shape public:double getArea()const;void print()const;,一.通过派生类对象调用同名成员函数,16,class Circle:public Shape public:Circle(int=0,int=0,double=0.0);double getArea()const;/返回面积 void print()const;private:int x,y;/圆心 double radius;/半径;,一.通过派生类对象调用同名成员函数,17,class Rectangle:public Shape public:Rectangle(int=
7、0,int=0);double getArea()const;/面积void print()const;private:int a,b;/长和宽;,一.通过派生类对象调用同名成员函数,18,/例1:shape.cpp#include using namespace std;#include shape.h double Shape:getArea()const cout基类的getArea函数,面积是;return 0.0;void Shape:print()const coutBase class Objectendl;,一.通过派生类对象调用同名成员函数,19,Circle:Circle(
8、int xValue,int yValue,double radiusValue)x=xValue;y=yValue;radius=radiusValue;double Circle:getArea()const coutCircle类的getArea函数,面积是;return 3.14159*radius*radius;void Circle:print()const cout center is;coutx=x y=y;cout;radius is radiusendl;,一.通过派生类对象调用同名成员函数,20,Rectangle:Rectangle(int aValue,int bVa
9、lue)a=aValue;b=bValue;double Rectangle:getArea()const coutRectangle类的getArea函数,面积是;return a*b;void Rectangle:print()const cout hight is a;coutwidth isbendl;,一.通过派生类对象调用同名成员函数,21,/例1:chapter9_1.cpp#include using namespace std;#include shape.hvoid main()Circle circle(22,8,3.5);Rectangle rectangle(10,1
10、0);cout 调用的是;coutcircle.getArea()endl;/静态联编 cout 调用的是;coutrectangle.getArea()endl;/静态联编,调用的是Circle类的getArea函数,面积是38.4845 调用的是Ractangle类的getArea函数,面积是100,一.通过派生类对象调用同名成员函数,22,通过派生类对象调用同名成员函数,可以有以下的结论:派生类对象可以直接调用本类中与基类成员函数同名的函数,不存在二义性;在编译时就能确定通过对象将调用哪个函数,属于静态联编。,Circle circle(22,8,3.5);Rectangle recta
11、ngle(10,10);circle.getArea();rectangle.getArea();,一.通过派生类对象调用同名成员函数,23,二.通过基类指针调用同名成员函数,例 9-2 用指针访问继承树中的同名函数/以下代码仅为示例,无法编译class B public:f();/基类class P:public B public:f();/派生类1class Q:public B public:f();/派生类2void main()B*b_ptr;P p;Q q;/定义基类的指针和派生类的对象b_ptr=/通过基类指针调用B:f(),24,二.通过基类指针调用同名成员函数,从继承的角度来
12、看,派生类对象是基类对象的一个具体的特例。或者说,派生类对象是某一种特定类型的基类对象。例如,Circle类是Shape类的公有继承,“圆”是“图形”的一种特例。或者说,圆是一种特定的图形,具有图形的基本特征。,25,在关于基类对象和派生类对象的操作上,可以允许以下的操作:可以用派生类对象给基类对象赋值;例:Circle circle(22,8,3.5);Rectangle rectangle(10,10);Shape a=circle;Shape b=rectangle;,二.通过基类指针调用同名成员函数,26,可以用派生类对象初始化基类的引用。例:Shape 通过该指针,可以访问基类的公有
13、成员。,二.通过基类指针调用同名成员函数,27,以下这些操作是不能进行的:不能用基类对象给派生类对象赋值;例:Shape sp;Circle r=sp;/error不能用基类对象的地址初始化派生类对象的指针;例:Shape sp;Circle*pc=/error,二.通过基类指针调用同名成员函数,28,例2 在例1所定义的类的基础上,观察通过派生类对象地址初始化的基类对象的指针访问getArea函数的结果。#include using namespace std;#include shape.h void main()Shape*shape_ptr;Circle circle(22,8,3.5
14、);Rectangle rectangle(10,10);shape_ptr=/静态联编,circle 对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是 0rectangle 对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是 0,29,程序运行结果表明:确实可以用派生类对象的地址初始化基类对象的指针;通过用派生类对象地址初始化的基类对象指针,只能调用基类的公有成员函数。在以上例子中,就是调用基类的getArea函数,而不是派生类的getArea函数。这种调用关系的确定,也是在编译的过程中完成的,属于静态联编。,
15、二.通过基类指针调用同名成员函数,30,2023/8/31,-30-,9.2.2 覆盖与静态联编,31,9.2.2 覆盖与静态联编,例9-3 修改例8-5,创建4个类,分别使用各个类的对象调用各类的同名函数,分别使用不同类的对象初始化基类指针,通过基类指针调用同名函数,观察同名函数调用结果。多文件结构:10个文件主函数Main03.cppGlobal03.h类TPoint的声明和定义:TPoint03.h和TPoint03.cpp类TShape的声明和定义:TShape03.h类TRect的声明和定义:TRect03.h类TCircle的声明和定义:TCircle03.h和TCircle03.
16、cpp类TEllipse的声明和定义:TEllipse03.h和TEllipse03.cpp,32,9.2.2 覆盖与静态联编,例9-3 重点:类层次结构:TShapeTRect,TShape TCircle TEllipse同名函数Draw();在主函数main中,通过各个类的对象调用Draw()函数,并且通过基类指针调用各个对象的Draw(),结果不同。,33,9.2.2 覆盖与静态联编,34,9.2.2 覆盖与静态联编,35,9.2.2 覆盖与静态联编,36,9.2.2 覆盖与静态联编,37,9.2.2 覆盖与静态联编,38,9.2.2 覆盖与静态联编,39,9.2.2 覆盖与静态联编,
17、40,第9章 多态性,41,9.3 虚函数与运行时的多态,在同名覆盖现象中,通过某个类的对象(对象指针、对象引用)调用同名函数,编译器会将该调用联编到该类的同名函数。通过基类对象指针(引用)是无法访问派生类普通函数的,即便这个指针(引用)是使用派生类对象初始化的。,42,9.3 虚函数与运行时的多态,通过指向基类的指针访问基类和派生类的同名函数,是实现运行时的多态的必要条件,但不是全部条件;如果希望利用基类的指针访问派生类的同名函数,必须将基类中的同名函数定义为虚函数。虚函数能够实现运行时的多态。,43,9.3.1 虚函数,在类的定义中声明虚函数,格式如下:virtual 返回值类型 函数名(
18、参数表);在函数原型中声明函数是虚函数后,具体定义这个函数时就不需要再说明它是虚函数了。在基类定义中直接定义虚函数的格式是:virtual 返回值类型 函数名(参数表)/函数体,44,9.3.1 虚函数,基类中的同名函数声明或定义为虚函数后,派生类的同名函数无论是不是用virtual来说明,都将自动地成为虚函数。从程序可读性考虑,一般都会在这些函数的声明或定义时,用virtual来加以说明。,45,2023/8/31,-45-,例 9-4 用指针+虚函数的形式实现动态联编class B public:virtual f();class P:public B public:f();class Q
19、:public B public:f();main()B*b_ptr;P p;Q q;b_ptr=/调用的是Q:f(),9.3.1 虚函数,46,2023/8/31,-46-,例 9-5 用引用+虚函数的形式实现动态联编class B public:virtual f();class P:public B public:f();class Q:public B public:f();main()P p;Q q;B/调用的是Q:f(),9.3.1 虚函数,47,例3:将例2进行修改,使得程序具有运行时的多态的效果。多文件结构 shape1.h:类的声明 shape1.cpp:类的定义和实现 ch
20、apter9_3.cpp:主函数,9.3.1 虚函数,48,例3:将例2进行修改,使得程序具有运行时的多态的效果。/例3:shape1.h#ifndef SHAPE_H#define SHAPE_Hclass Shape public:virtual double getArea()const;void print()const;,9.3.1 虚函数,49,class Circle:public Shape public:Circle(int=0,int=0,double=0.0);virtual double getArea()const;/double getArea()const;voi
21、d print()const;private:int x,y;double radius;,9.3.1 虚函数,50,class Rectangle:public Shape public:Rectangle(int=0,int=0);virtual double getArea()const;/double getArea()const;void print()const;private:int a,b;#endif,9.3.1 虚函数,51,/例3:shape1.cpp#include using namespace std;#include shape1.h double Shape:g
22、etArea()const cout基类的getArea函数,面积是;return 0.0;void Shape:print()constcoutBase class Objectendl;,9.3.1 虚函数,52,Circle:Circle(int xValue,int yValue,double radiusValue)x=xValue;y=yValue;radius=radiusValue;double Circle:getArea()const coutCircle类的getArea函数,面积是;return 3.14159*radius*radius;void Circle:pri
23、nt()const cout center is;coutx=x y=y;cout;radius is radiusendl;,9.3.1 虚函数,53,Rectangle:Rectangle(int aValue,int bValue)a=aValue;b=bValue;double Rectangle:getArea()const coutRectangle类的getArea函数,面积是;return a*b;void Rectangle:print()const cout hight is a;coutwidth isbendl;,9.3.1 虚函数,54,/例3:9_3.cpp#inc
24、lude using namespace std;#include shape1.h void main()Shape*shape_ptr;Circle circle(22,8,3.5);Rectangle rectangle(10,10);shape_ptr=,circle 对象初始化shape_ptr指针访问的getArea函数是Circle类的getArea函数,面积是 38.4845rectangle 对象初始化shape_ptr指针访问的getArea函数是Rectangle类的getArea函数,面积是 100,一种运行时决定的多态性,55,9.3.1 虚函数,一种运行时决定的多态
25、性,56,11.3.1 虚函数,要实现运行时的多态,需要以下条件:必须通过指向基类对象的指针访问和基类成员函数同名的派生类成员函数;shape_ptr=派生类的继承方式必须是公有继承;基类中的同名成员函数必须定义为虚函数。,57,9.3.1 虚函数,虚函数必须正确的定义和使用。否则,即使在函数原型前加了virtual的说明,也可能得不到运行时多态的特性。必须首先在基类中声明虚函数。在多级继承的情况下,也可以不在最高层的基类中声明虚函数。例如在第二层定义的虚函数,可以和第三层的虚函数形成动态联编。但是,一般都是在最高层的基类中首先声明虚函数。,58,9.3.1 虚函数,基类和派生类的同名函数,必
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c程序设计基础 程序设计 基础 第九 多态性
链接地址:https://www.31ppt.com/p-5896071.html