《面向对象程序设计》多态.ppt

上传人:牧羊曲112 文档编号:6529533 上传时间:2023-11-09 格式:PPT 页数:45 大小:315.50KB
返回 下载 相关 举报
《面向对象程序设计》多态.ppt_第1页
第1页 / 共45页
《面向对象程序设计》多态.ppt_第2页
第2页 / 共45页
《面向对象程序设计》多态.ppt_第3页
第3页 / 共45页
《面向对象程序设计》多态.ppt_第4页
第4页 / 共45页
《面向对象程序设计》多态.ppt_第5页
第5页 / 共45页
点击查看更多>>
资源描述

《《面向对象程序设计》多态.ppt》由会员分享,可在线阅读,更多相关《《面向对象程序设计》多态.ppt(45页珍藏版)》请在三一办公上搜索。

1、2023/11/9,北京科技大学计算机系,1,C+大学基础教程,第10章 多态性 北京科技大学 计算机系,2023/11/9,北京科技大学计算机系,-2-,第十一章 多态性,11.1 多态性的概念 11.2 继承中的静态联编 11.3 虚函数和运行时的多态 11.4 纯虚函数和抽象类,2023/11/9,北京科技大学计算机系,-3-,多态性的概念,多态性是面向对象程序设计的重要特征之一。多态性是指发出同样的消息被不同类型的对象接收时导致不同的行为。擦 地板 窗户 脸,采用不同的工具,不同的方式。在c+中可以用相同的函数名,不同的函数操作来实现,函数重载:函数名相同,但是函数的参数个数或者参数类

2、型不同,或者const标识,2023/11/9,北京科技大学计算机系,-4-,面向对象程序设计中多态的表现,总的来说,不同对象对于相同的消息有不同的响应,就是面向对象程序设计中的多态性。具体在程序中,多态性有两种表现的方式:同一个对象调用名字相同、但是参数不同的函数,表现出不同的行为。重载多态 在同一个类中定义的重载函数的调用。不同的对象调用名字和参数都相同的函数,表现出不同的行为。-运行多态 在派生类中重复定义 同名覆盖:使用派生类的定义 作用域符的使用:使用基类的定义,2023/11/9,北京科技大学计算机系,-5-,继承和派生中的多态,class A public:void f();cl

3、ass C:public A public:void f();void f(int x);,如果声明:C c1;则 c1.f();c1.f(int x);c1.A:f(),2023/11/9,北京科技大学计算机系,-6-,面向对象程序设计中多态的表现,面向对象程序设计中多态性表现为以下几种形式:重载多态:通过调用相同名字的函数,表现出不同的行为。运行多态:通过基类的指针或引用,调用不同派生类的同名函数,表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。模板多态,也称为参数多态:通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。,2023/

4、11/9,北京科技大学计算机系,-7-,多态性的概念,例1int max(int a,int b)return ab?a:b;int max(int a,int b,int c)return(int t=(ab?a:b)c?t:c;float max(float a,float b)void main()int a,b,f;float c,d;max(a,b);max(c,d);,在编译的时候,确定调用的是哪个max,静态联编;,函数重载,2023/11/9,北京科技大学计算机系,-8-,继承和派生中的多态,class A public:void f();class C:public A pu

5、blic:void f();void f(int x);,如果声明:C c1;则 c1.f();c1.f(int x);c1.A:f(),静态联编;,2023/11/9,北京科技大学计算机系,-9-,派生类对象调用同名函数,对于派生类对象调用成员函数,可以有以下的结论:派生类对象可以直接调用本类中与基类成员函数同名的函数,不存在二义性;在编译时就能确定对象将调用哪个函数,属于静态联编,不属于运行时的多态。,2023/11/9,北京科技大学计算机系,-10-,赋值兼容原则,一个公有派生类的对象在使用上可以被当作基类的对象,反之则禁止。具体表现在:derived d;base b;派生类的对象可以

6、被赋值给基类对象。b=d;派生类的对象可以初始化基类的引用。base,2023/11/9,北京科技大学计算机系,-11-,赋值兼容规则举例-多态是否具有动态性,#include class B0/基类B0声明 public:void display()coutB0:display()endl;/公有成员函数;,定义了基类,2023/11/9,北京科技大学计算机系,-12-,class B1:public B0 public:void display()coutdisplay();/对象指针-成员名,定义了类族,定义了基类的对象指针,2023/11/9,北京科技大学计算机系,-13-,void

7、main()/主函数B0 b0;/声明B0类对象B1 b1;/声明B1类对象D1 d1;/声明D1类对象B0*p;/声明B0类指针p=,B0:display(),B0:display(),B0:display(),2023/11/9,北京科技大学计算机系,-14-,概念复习,根据赋值兼容规则,下列哪一个陈述是正确的?【】公有派生类的对象可以赋值给基类的对象。任何派生类的对象可以赋值给基类的对象。基类的对象可以赋值给派生类的对象。间接基类的对象可以赋值给直接基类的对象。,2023/11/9,北京科技大学计算机系,-15-,多态的实现:联编,联编(Binding绑定):确定具体的操作对象。程序自身

8、彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。静态联编:编译的多态动态联编:运行时的多态,int a;main()int a;a=7;,2023/11/9,北京科技大学计算机系,-16-,静态联编与动态联编,静态联编(静态束定)联编工作出现在编译阶段,用对象名或者类名来限定要调用的函数。动画动态联编联编工作在程序运行时执行,在程序运行时才确定将要调用的函数。,例,例,2023/11/9,北京科技大学计算机系,-17-,#includeclass Point public:Point(double i,double j)x=i;y=j;double Area()return 0

9、.0;private:double x,y;class Rectangle:public Point public:Rectangle(double i,double j,double k,double l);double Area()return w*h;private:double w,h;,静态联编例,2023/11/9,北京科技大学计算机系,-18-,Rectangle:Rectangle(double i,double j,double k,double l):Point(i,j)w=k;h=l;void fun(Point 运行结果:Area=0,X,inaceesible,Y,i

10、naceesible,W,private,H,private,15.0,25.0,3.0,5.2,2023/11/9,北京科技大学计算机系,-19-,例11.1 定义Circle类和Rectangle类为Shape类的派生类,通过Circle类和Rectangle类的对象调用重载函数getArea()显示对象的面积。/例11.1:shape.h#ifndef SHAPE_H#define SHAPE_Hclass Shape public:double getArea()const;void print()const;/Shape类定义结束,基类Shape的定义,课下读懂!,2023/11/9

11、,北京科技大学计算机系,-20-,#includeclass Point public:Point(double i,double j)x=i;y=j;virtual double Area()return 0.0;private:double x,y;class Rectangle:public Point public:Rectangle(double i,double j,double k,double l);virtual double Area()return w*h;private:double w,h;void fun(Point,动态联编例,虚函数,运行结果:Area=375,

12、运行结果:Area=0,声明虚函数的目的就是通知编译器,碰到函数名时不要马上选择静态联编,也有可能时动态联编的。,2023/11/9,北京科技大学计算机系,21,虚函数和运行时的多态,2023/11/9,北京科技大学计算机系,-22-,虚函数,虚函数是动态联编的基础。是非静态的成员函数。在类的声明中,在函数原型之前写virtual。virtual 只用来说明类声明中的原型,不能用在函数实现时。具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。本质:不是重载声明而是覆盖。调用方式:通过基类指针或引用,执行时会根据指针或引用指向的对象的类,决定调用哪个函数。,2023

13、/11/9,北京科技大学计算机系,-23-,例,#include class B0/基类B0声明public:/外部接口virtual void display()coutB0:display()endl;/虚成员函数;,2023/11/9,北京科技大学计算机系,-24-,class B1:public B0/公有派生 public:void display()coutB1:display()endl;class D1:public B1/公有派生 public:void display()coutD1:display()endl;,2023/11/9,北京科技大学计算机系,-25-,void

14、 fun(B0*ptr)/普通函数 ptr-display();void main()/主函数B0 b0,*p;/声明基类对象和指针B1 b1;/声明派生类对象D1 d1;/声明派生类对象p=/调用派生类D1函数成员,B0:display(),B1:display(),D1:display(),2023/11/9,北京科技大学计算机系,-26-,void fun(B0 ptr)/普通函数 ptr.display();void main()/主函数B0 b0,*p;/声明基类对象和指针B1 b1;/声明派生类对象D1 d1;/声明派生类对象b0=b0;fun(b0);/调用基类B0函数成员b0=

15、b1;fun(b0);/调用基类B0函数成员b0=d1;fun(b0);/调用基类B0函数成员,B0:display(),B0:display(),B0:display(),2023/11/9,北京科技大学计算机系,-27-,例11.3 将例11.2进行修改,使得程序具有运行时的多态的效果。/例11.3:shape1.h#ifndef SHAPE_H#define SHAPE_Hclass Shape public:virtual double getArea()const;void print()const;/Shape类定义结束,基类Shape的定义,例11.3 课下读懂!,2023/11

16、/9,北京科技大学计算机系,-28-,10.3.1 虚函数,要实现运行时的多态,需要以下条件:必须通过指向基类对象的指针访问和基类成员函数同名的派生类成员函数;或者用派生类对象初始化的基类对象的引用访问和基类成员函数同名的派生类成员函数;派生类的继承方式必须是公有继承;基类中的同名成员函数必须定义为虚函数。,2023/11/9,北京科技大学计算机系,-29-,例10.4 虚函数的正确使用。分析以下程序,编译时哪个语句会出现错误?为什么?将有错误的语句屏蔽掉以后,程序运行结果如何?其中哪些调用是静态联编,哪些是动态联编?#include class BBpublic:virtual void v

17、f1()coutBB:vf1被调用n;virtual void vf2()coutBB:vf2被调用n;void f()coutBB:f被调用n;class DD:public BBpublic:virtual void vf1()coutDD:vf1被调用n;void vf2(int i)coutiendl;void f()coutDD:fn被调用;,有虚函数的基类,派生类,void main()DD d;BB*bp=,2023/11/9,北京科技大学计算机系,-30-,void main()DD d;BB*bp=,将这个语句注释掉后,运行结果将显示:DD:vf1被调用BB:vf2被调用BB

18、:f被调用,函数调用bp-vf2(10);是错误的。因为派生类的vf2函数和基类的vf2函数的参数不同,派生类的vf2就不是虚函数,而是重载。,其中bp-vf1()调用是动态联编。bp-vf2()是静态联编。bp-f()也是静态联编。,2023/11/9,北京科技大学计算机系,-31-,10.3.2 虚函数的使用,虚函数必须正确的定义和使用。否则,即使在函数原型前加了virtual的说明,也可能得不到运行时多态的特性。必须首先在基类中声明虚函数。在多级继承的情况下,也可以不在最高层的基类中声明虚函数。例如在第二层定义的虚函数,可以和第三层的虚函数形成动态联编。但是,一般都是在最高层的基类中首先

19、声明虚函数。,2023/11/9,北京科技大学计算机系,-32-,11.3.2 虚函数的使用,基类和派生类的同名函数,必须函数名、返回值、参数表全部相同,才能作为虚函数来使用。否则,即使函数用virtual来说明,也不具有虚函数的行为。静态成员函数不可以声明为虚函数。构造函数也不可以声明为虚函数。析构函数可以声明为虚函数,即可以定义虚析构函数。,2023/11/9,北京科技大学计算机系,-33-,10.3.3 虚析构函数,如果用动态创建的派生类对象的地址初始化基类的指针,创建的过程不会有问题:仍然是先调用基类构造函数,再执行派生类构造函数。但是,在用delete运算符删除这个指针的时候,由于指

20、针是指向基类的,通过静态联编,只会调用基类的析构函数,释放基类成员所占用的空间。而派生类成员所占用的空间将不会被释放。,2023/11/9,北京科技大学计算机系,-34-,#include using namespace std;class Shape public:Shape()coutShape类构造函数被调用n;Shape()coutShape类析构函数被调用n;/Shape类定义结束class Circle:public Shape/派生类Circle的定义public:Circle(int xx=0,int yy=0,double rr=0.0)x=xx;y=yy;radius=rr

21、;coutCircle类构造函数被调用n;Circle()coutCircle类析构函数被调用n;private:int x,y;/圆心座标double radius;/圆半径;/派生类Circle定义结束void main()Shape*shape_ptr;shape_ptr=new(Circle)(3,4,5);delete shape_ptr;,例11.5 定义简单的Shape类和Circle类,观察基类指针的创建和释放时如何调用构造函数和析构函数。,基类Shape的定义,主函数,程序运行后在屏幕上显示:Shape类构造函数被调用Circle类构造函数被调用Shape类析构函数被调用,2

22、023/11/9,北京科技大学计算机系,-35-,10.3.3 虚析构函数,为了解决派生类对象释放不彻底的问题,必须将基类的析构函数定义为虚析构函数。格式是在析构函数的名字前添加virtual关键字。函数原型如下:virtual Shape();此时,无论派生类析构函数是不是用virtual来说明,也都是虚析构函数。再用delete shape_ptr来释放基类指针时,就会通过动态联编调用派生类的析构函数。,2023/11/9,北京科技大学计算机系,-36-,11.3.3 虚析构函数,将例11.5程序中的Shape析构函数作以上修改后,运行的结果将是:Shape类构造函数被调用Circle类构

23、造函数被调用Circle类析构函数被调用Shape类析构函数被调用,2023/11/9,北京科技大学计算机系,-37-,11.4 纯虚函数和抽象类,2023/11/9,北京科技大学计算机系,-38-,抽象类的一般形式,带有纯虚函数的类称为抽象类:class 类名 virtual 类型 函数名(参数表)=0;/纯虚函数.;,一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。,2023/11/9,北京科技大学计算机系,-39-,作用,抽象类为抽象和设计的目的而建立,将有关的数据和行

24、为组织在一个继承层次结构中,保证派生类具有要求的行为。对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。,2023/11/9,北京科技大学计算机系,-40-,注意,抽象类只能作为基类来使用。不能声明抽象类的对象。构造函数不能是虚函数,析构函数可以是虚函数。eg.class Apublic:virtual void fun1()=0;A obj;,2023/11/9,北京科技大学计算机系,-41-,例,1)了解纯虚函数的定义2)了解纯虚类只能作为基类,派生其他类而用class Apublic:A()fun();virtual void fun()cout“A:fun”endl;cla

25、ss B:public A public:B()fun();void fun()cout“B:fun”endl;B b;,1 构造函数中调用的虚函数不具有动态联编,必然是调用的本类中的函数。2 Virtual void fun()=0;,virtual void fun()=0;,2023/11/9,北京科技大学计算机系,-42-,class Apublic:void foo()bar();private:virtual void bar().;class B:public Aprivate:virtual void bar().;void bar1(A*a)a-foo();,bar函数中调用

26、a对象的foo,foo中调用虚函数bar,调用的是哪个bar。,2023/11/9,北京科技大学计算机系,-43-,一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。例如:class Apublic:A()foo();/在这里,无论如何都是A:foo()被调用!A()foo();/同上virtual void foo();class B:public Apublic:virtual void foo();void bar()A*a=new B;delete a;,2023/11/9,北京科技大学计算机系

27、,-44-,多态有什么用?在面向对象的编程中,首先会针对数据进行抽象(确定基类)和继承(确定派生类),构成类层次。类层次的使用者在使用它们的时候,如果仍然在需要基类的时候写针对基类的代码,在需要派生类的时候写针对派生类的代码,就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变(增加了新类),都需要使用者“知道”(针对新类写代码)。这样就增加了类层次与其使用者之间的耦合,有人把这种情况列为程序中的“bad smell”之一。,2023/11/9,北京科技大学计算机系,-45-,总结,多态性是面向对象程序设计最重要的特点之一。本章介绍了多态性中最重要的两个表现:运行多态和参数多态。参数多态就是模板的使用。运行多态的表现就是:一种形态的语句:通过基类指针访问基类和派生类的同名函数;多种条件的执行:用不同派生类对象的地址初始化这个基类指针;,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号