第14章多态性

上传人:无*** 文档编号:243974559 上传时间:2024-10-01 格式:PPT 页数:47 大小:727.50KB
返回 下载 相关 举报
第14章多态性_第1页
第1页 / 共47页
第14章多态性_第2页
第2页 / 共47页
第14章多态性_第3页
第3页 / 共47页
点击查看更多>>
资源描述
,Click to edit Master text styles,Second level,Third level,Fourth level,Fifth level,*,Click to edit Master title style,第,14,章 多态性,南开大学非计算机专业理工科,面向对象程序设计课程,课程内容安排(,2,课时),多态性的概念,虚函数,抽象类,应用实例,小结,学习指导,2,14.1,多态性的概念,在介绍如何在程序中实现多态之前,我们先给出一个实例来理解什么是多态。,【,例,14-1】,未实现多态性的程序。,#include,using namespace std;,class Shape,public:,void Draw(),cout,Draw shape,endl,;,;,class Circle:public Shape,public:,void Draw(),cout,Draw circle,endl,;,;,3,14.1,多态性的概念,class Triangle:public Shape,public:,void Draw(),cout,Draw triangle,endl,;,;,class Rectangle:public Shape,public:,void Draw(),cout,Draw rectangle,endl,;,;,void,func(Shape,&s),s.Draw,();,4,14.1,多态性的概念,int,main(),Circle c;,Triangle t;,Rectangle r;,func(c,);,func(t,);,func(r,);,return 0;,运行结果为:,Draw shape,Draw shape,Draw shape,5,14.1,多态性的概念,显然,我们并不希望看到这样的结果。我们希望当执行,func(c,),时调用,Circle,类的,Draw(),函数,当执行,func(t,),时调用,Triangle,类的,Draw(),函数,当执行,func(r,),时调用,Rectangle,类的,Draw(),函数。这种能够根据对象的实际类型来调用该对象所属类的函数、而不是每次都调用基类中函数的特性,就是本章所要介绍的,多态性。,6,14.2,虚函数,14.2.1,先期绑定和动态绑定,传统的面向结构的编译器所产生的函数调用,采用“,先期绑定,”的方式。所谓“绑定”就是建立函数调用和函数本体的关联。如果绑定发生于程序运行之前(由编译器和连接器完成),则称为“,先期绑定,”。,面向对象程序设计语言采用“,后期绑定,”,也可叫“,动态绑定,”技术,即当调用某个对象的函数时,应该被执行的程序代码会根据对象的具体类型在执行期被确定下来。这是实现多态性的技术保证。,想要实现多态,就要进行“后期绑定”,在,C+,中,实现“后期绑定”的机制是,虚函数,。,7,14.2,虚函数,14.2.2,虚函数的工作方式,8,14.2,虚函数,在图,14-1,所示形状的类层次中,,Circle,类、,Triangle,类、,Rectangle,类都是从基类,Shape,派生出来的。每个类都有它自己的,Draw(),函数,并且绘制每种形状的,Draw(),函数都大不相同。当需要绘制形状时,把各派生类对象统一作为基类,Shape,的对象处理是一种很好的方法。只需要简单地调用基类,Shape,的,Draw(),函数,而不必关心对象的实际类型,程序会动态地确定(即在执行时确定)调用哪个派生类的,Draw(),函数。为了使这种行为可行,把基类中的函数,Draw(),声明为虚函数,然后在每个派生类中重新定义,Draw(),使之能够绘制合适的形状。,虚函数,的声明方法是在基类的函数原型前加上关键字,virtual,。,9,14.2,虚函数,【,例,14-2】,将,例,14-1,进行简单的修改,在基类的,Draw(),函数前加上关键字,virtual,#include,using namespace std;,class Shape,public:,virtual void Draw(),/,将,Draw(),函数声明为虚函数,cout,Draw shape,endl,;,;,10,14.2,虚函数,class Circle:public Shape,public:,virtual void Draw(),/,此处的,virtual,可以没有,不影响,Draw(),的虚函数性质,cout,Draw circle,endl,;,;,class Triangle:public Shape,public:,void Draw(),cout,Draw triangle,endl,;,;,11,14.2,虚函数,class Rectangle:public Shape,public:,void Draw(),cout,Draw rectangle,endl,;,;,void,drawShape(Shape,&s),s.Draw,();,int,main(),Circle c;,Triangle t;,Rectangle r;,drawShape,(c);,drawShape,(t);,drawShape,(r);,return 0;,运行结果为:,Draw circle,Draw triangle,Draw rectangle,12,14.2,虚函数,提示:,Draw(),函数在基类,Shape,中声明为,virtual,,该虚函数的性质自动地向下带给其派生类,所以派生类中可以省略,virtual,关键字。虽然如此,但有时为了提高程序的可读性,可以在每一层中显式地声明这些虚函数。,只有将派生类对象赋给基类对象引用或将派生类对象地址赋给基类对象指针时,才能够表现出多态性。如果将派生类对象赋给基类对象,那么通过基类对象必然是调用基类中的函数。,13,14.2,虚函数,有了虚函数,以后如果要求,drawShape,(),增加一个绘制新形状的功能,只要简单的从,Shape,类派生新类就可以了,而,drawShape,(),函数不需要做任何改动。例如,增加一个五边形类:,class,Patagon,:public Shape,public:,void Draw(),cout,Draw,patagon,endl,;,;,只要新增加的形状是由,Shape,类派生出来的,则,drawShape,(),函数不需要做任何修改,在主函数中如果出现如下语句,就会调用,Patagon,类对象的,Draw(),函数。,int,main(),Patagon,p;,drawShape(p,);,return 0;,可见,多态性使应用程序代码大大简化,使程序扩充变得更加简单。,14,14.2,虚函数,14.2.3,虚析构函数,通过上面的介绍,我们已经了解了多态性和虚函数的工作方式。我们再来看一个例子:,【,例,14-3】Employee,类及其派生类,Manager,。,#include,using namespace std;,class Employee,public:,Employee(char,*,pName,long,no),m_name,=new charstrlen(pName)+1;,strcpy(m_name,pName,);,m_number,=no;,15,14.2,虚函数,Employee(),cout,Employee destructed!,endl,;,delete,m_name,;,virtual void,DispInfo,(),cout,Name is,m_name,;,cout,No.is,m_number,endl,;,protected:,char*,m_name,;,long,m_number,;,;,16,14.2,虚函数,class,Manager:public,Employee,public:,Manager(char,*,pName,long,no,char*,dept):Employee(pName,no,),m_department,=new charstrlen(dept)+1;,strcpy(m_department,dept,);,Manager(),cout,Manager destructed!,endl,;,delete,m_department,;,17,14.2,虚函数,virtual void,DispInfo,(),cout,Name is,m_name,;,cout,No.is,m_number,;,cout,Department is,m_department,DispInfo,();/,通过基类指针,调用派生类相应虚函数,delete,pe,;/,释放指针,pe,所指向的地址,return 0;,18,14.2,虚函数,在,Employee,类和,Manager,类中,都包含有指针类型的成员,应该利用析构函数显示地释放这些指针所指向的内存空间。主函数中创建的是派生类,Manager,的对象,所以应该调用,Manager,类的析构函数释放空间。但是,通过观察运行结果,我们可以看到,被调用的是基类,Employee,的析构函数,也就是说,,Manager,类对象中,只有属于基类,Employee,的那部分指针成员被释放了,而,Manager,类中新添加的指针成员没有被释放,这样就会导致内存泄露问题。,要解决上述问题,可以将基类中的析构函数声明为虚析构函数。这样就会使所有派生类的析构函数自动成为虚析构函数(虽然它们与基类析构函数名不同)。这时,如果像上面那样使用,delete,运算符时,系统会根据对象的实际类型调,用相应类的析构函数。,19,14.2,虚函数,【,例,14-4】,将例,14-3,中的析构函数声明为虚析构函数。,virtual Employee()/,将析构函数声明为虚析构函数,cout,Employee destructed!,endl,;,delete,m_name,;,运行结果为:,Name is Alice,No.is 2,Department is Market,Employee destructed!,Manager destructed!,提示:,在调用派生类对象的析构函数后,会自动调用基类对象的析构函数来释放基类中定义的指针成员。,20,14.2,虚函数,14.2.4,错误地使用虚函,如果在基类中的虚函数跟子类中的函数只是名字相同,而参数不同,或者返回类型不同,即使写上了,virtual,关键字,也不进行“后期绑定”。,【,例,14-5】,下面程序中,,基类,Based,的成员函数,fn(),和派生类,SubClass,的成员函数,fn(),参数类型不同。,#include,using namespace std;,class Base,public:,virtual void,fn(int,x),cout,In Base class,int,x=x,endl,;,;,21,14.2,虚函数,class,SubClass,:public Base,public:,virtual void,fn(float,x),cout,In,SubClass,float x=x,endl,;,;,void,test(Base,&b),int,i=1;,b.fn(i,);,float f=2.0;,b.fn(f,);,22,14.2,虚函数,int,main(),Base,bc,;,SubClass,sc;,cout,Calling,test(bc)n,;,test(bc,);,cout,Calling,test(sc)n,;,test(sc,);,return 0;,运行结果为:,Calling,test(bc,),In Base class,int,x=1,In Base class,int,x=2,Calling,te
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


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

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


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