多态性和虚函数_第1页
多态性和虚函数_第2页
多态性和虚函数_第3页
多态性和虚函数_第4页
多态性和虚函数_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

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

文档简介

多态性和虚函数第一页,共二十八页,编辑于2023年,星期五9.1多态性的概念多态性是指不同类的对象对于同一消息的处理具有不同的实现。多态性在C++中表现为同一形式的函数调用,可能调用不同的函数实现。从系统实现的角度看,C++的多态性分为两类,一类称为编译时刻多态性,另一类称为运行时刻多态性,也称动态多态性。第二页,共二十八页,编辑于2023年,星期五9.1.1编译时刻的多态性C++编译时多态性通过重载(函数重载和运算符重载)来实现【例9.1】编译时刻的多态性——运算符重载:下面这段程序建立Rectangle类和Cuboid类,并重载运算符“+=”,使之能用于相应类对象的运算。第三页,共二十八页,编辑于2023年,星期五#include<iostream>usingnamespacestd;classRectangle//定义矩形类{public:Rectangle(doublew=0,doublel=0); //缺省构造函数

voidset_wl(doublew,doublel);doubleget_w()const;doubleget_l()const;doublearea();~Rectangle(){}; //析构函数

Rectangle&operator+=(Rectangle&rec_add) //重载运算符+={width+=rec_add.width;length+=rec_add.length;return*this; //返回当前对象

}protected:intwidth;intlength;};第四页,共二十八页,编辑于2023年,星期五Rectangle::Rectangle(doublew,doublel):width(w),length(l){}voidRectangle::set_wl(doublew,doublel){width=w;length=l;}doubleRectangle::get_w()const{returnwidth;}doubleRectangle::get_l()const{returnlength;}doubleRectangle::area(){returnwidth*length;}classCuboid:publicRectangle//定义长方体类{public:Cuboid(doublew=0,doublel=0,doubleh=0);voidset_wlh(doublew,doublel,doubleh);doubleget_h()const;doublearea();Cuboid&operator+=(Cuboid&cub_add){width+=cub_add.width;length+=cub_add.length;height+=cub_add.height;return*this; //返回当前对象

}protected:doubleheight;};第五页,共二十八页,编辑于2023年,星期五Cuboid::Cuboid(doublew,doublel,doubleh):Rectangle(w,l),height(h){}voidCuboid::set_wlh(doublew,doublel,doubleh){width=w;length=l;height=h;}doubleCuboid::get_h()const{returnheight;}doubleCuboid::area(){return2*(width*length+width*height+length*height);}//求长方体的表面积intmain(){Rectanglerec1(1,2);Rectanglerec2;Cuboidcub1(1,2,3);Cuboidcub2;rec2.set_wl(2,4);cub2.set_wlh(5,10,15);cout<<"rec1:(width="<<rec1.get_w()<<",length="<<rec1.get_l()<<")"<<endl;cout<<"rec2:(width="<<rec2.get_w()<<",length="<<rec2.get_l()<<")"<<endl;

第六页,共二十八页,编辑于2023年,星期五rec2+=rec1; //调用Rectangle类的重载运算符:+=cout<<"rec2.width=("<<rec2.get_w()<<",length="<<rec2.get_l()<<")"<<endl;cout<<"cub1:(width="<<cub1.get_w()<<",length="<<cub1.get_l()<<",height=";cout<<cub1.get_h()<<")"<<endl;cout<<"cub2:(width="<<cub2.get_w()<<",length="<<cub2.get_l()<<",height=";cout<<cub2.get_h()<<")"<<endl;cub2+=cub1; //调用Cuboid类的重载运算符:+=cout<<"cub2:(width="<<cub2.get_w()<<",length="<<cub2.get_l()<<",height=";cout<<cub2.get_h()<<")"<<endl;cout<<"rec2'sareais"<<rec2.area()<<endl;cout<<"cub2'sareais"<<cub2.area()<<endl;return0;}第七页,共二十八页,编辑于2023年,星期五9.1.2运行时刻的多态性

运行时刻多态性的实现是指在程序运行过程中根据具体情况来确定调用的是哪一个函数,它是通过动态联编机制实现的第八页,共二十八页,编辑于2023年,星期五【例9.2】运行时刻的多态性运行时的多态性。仍然用例9.1中定义的Rectangle类和Cuboid类。//*************ex9_2.cpp*************//此处加上例9.1中定义的Rectangle类和Cuboid类。intmain(){Rectangle*r;Cuboidcub3(1,2,3);cout<<"cub3=("<<cub3.get_w()<<","<<cub3.get_l()<<",";cout<<cub3.get_h()<<")"<<endl;r=&cub3; //用基类指针指向派生类对象

cout<<"cub3'sareais(*r)"<<r->area()<<endl;cout<<"cub3'sareais(cub3)"<<cub3.area()<<endl;return0;}程序的运行结果如下:cub3=(1,2,3)cub3'sareais(*r)2cub3'sareais(cub3)22第九页,共二十八页,编辑于2023年,星期五运行时刻的多态性是面向对象的一个非常重要的特征,再来看一个在结构化编程中的例子:【例9.3】下面这段程序是利用多分支结构编程模拟实现绘制图形的函数。//********ex9_3.cpp*********voiddraw(intobj_figure){switch(obj_figure)

case0://rectangle

draw_rectangle();

//cout<<”drawrectangle”<<endl;

break;

case1://triangle

draw_triangle();

//cout<<”drawtriangle”<<endl;

break;

case2://circle

draw_circle();

//cout<<"draw_circle"<<endl;

break;}这种编程方式使得程序的可维护性和可扩充性都变得很差。那么有没有更好的方法实现上述例子?看看下面的程序段:voiddraw(void*f){

(*f)();}第十页,共二十八页,编辑于2023年,星期五9.2虚函数虚函数的作用 虚函数从表现形式看是指那些被virtual关键字修饰的成员函数。类的一个成员函数如果被说明为虚函数,表明它目前的具体实现仅是一种适用于当前类的实现,而在该类的继承层次链条中有可能重新定义这个成员函数的实现,即这个虚函数可能会被派生类的同名函数所覆盖(override)。第十一页,共二十八页,编辑于2023年,星期五例:使用虚函数的例子#include<iostream>usingstd::cout;usingstd::endl;classfigure{public:virtualvoiddraw()//将draw()定义为虚函数{cout<<"drawfigure"<<endl;}};classrectangle:publicfigure{voiddraw(){cout<<"drawrectangle"<<endl;}};classtriangle:publicfigure{voiddraw(){cout<<"drawtriangle"<<endl;}};第十二页,共二十八页,编辑于2023年,星期五intmain(){figure*f;rectangler1;trianglet1;f=&r1;//基类指针f指向派生类对象r1f->draw();//调用r1的成员函数draw()f=&t1;//基类指针f指向派生类对象t1f->draw();//调用t1的成员函数draw()return0;}程序运行结果:第十三页,共二十八页,编辑于2023年,星期五虚函数的使用虚函数的实现机制和调用方式与非虚函数不同,虚函数的使用需要注意以下几点:1.虚函数的声明 只能将类的成员函数声明为虚函数,而不能将类外的普通函数声明为虚函数。虚函数的作用是允许在派生类中对基类的虚函数重新定义,因而它只能用于类的继承层次结构中。2.虚函数的访问权限 派生类中虚函数的访问权限并不影响虚函数的动态联编,如下面的例9.5,其中派生类CDerived中重新定义了虚函数F4(),在程序的运行中由于虚函数的机制,在CBase::F3()中调用F4()时会调用CDerived::F4(),而该函数的访问权限是私有的。3.成员函数中调用虚函数 在类的成员函数中可以直接调用相应类中定义或重新定义的虚函数,分析这类函数的调用次序时要注意成员函数的调用一般是隐式调用,应该将其看成是通过this指针的显式调用。第十四页,共二十八页,编辑于2023年,星期五【例9.5】在成员函数中调用虚函数//*********ex9_5.cpp***********#include<iostream>usingnamespacestd;classCBase{public:voidF1(){cout<<"=>CBase-F1=>";F2();}voidF2(){cout<<"CBase-F2=>";F3();}virtualvoidF3(){cout<<"CBase-F3=>";F4();//即this->F4()}virtualvoidF4(){cout<<"CBase-F4=>";}};第十五页,共二十八页,编辑于2023年,星期五classCDerived:publicCBase{private:virtualvoidF4(){cout<<"Derived-F4=>out"<<endl;}public:voidF1(){cout<<"=>Derived-F1=>";CBase::F2();}voidF2(){cout<<"=>Derived-F2=>";F3();//即this->F3()}};intmain(){CBase*pB;CDerivedObj;程序运行结果:

pB=&Obj;pB->F1();Obj.F1();return0;}第十六页,共二十八页,编辑于2023年,星期五9.3纯虚函数与抽象类纯虚函数在程序设计中,通常会在类层次的顶层以虚函数的形式给出该类层次所提供的某些操作的统一接口,由于层次较高,有些操作无法(也无必要)给出具体的实现,对于这种情况可以不对虚函数的实现进行定义,而将它们说明为纯虚函数。纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是:virtual<函数类型><函数名>(参数表列)=0;第十七页,共二十八页,编辑于2023年,星期五抽象类具有纯虚函数的类无法用于创建对象,因为它的纯虚函数无函数体,所以又把这种含有纯虚函数的类称为抽象类。抽象类的主要作用是为一个族类提供统一的公共接口,用户在这个基础上根据自己的需要定义出功能各异的派生类,以有效地发挥多态的特性。第十八页,共二十八页,编辑于2023年,星期五使用抽象类时应注意以下问题:⑴抽象类只能用作其它类的基类,不能建立抽象类的对象。因为它的纯虚函数没有定义功能。⑵抽象类不能用作参数类型、函数的返回类型或显式转换的类型。⑶可以声明抽象类的指针和引用,通过它们,可以指向并访问派生类对象,从而访问派生类的成员。⑷如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,那么这些函数就被赋予了功能,可以被调用。这个派生类就不是抽象类,而是可以用来定义对象的具体类。如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。第十九页,共二十八页,编辑于2023年,星期五9.4抽象类的实例一个抽象类就是一个界面。类层次结构是一种逐步递增地建立类的方式。有些抽象类也提供了重要的功能,支持进一步向上构造。类层次结构中的各个类一方面为用户提供了有用的功能,同时也作为实现更高级或者更特殊的类的构造块。这种层次结构对于支持以逐步求精方式进行的程序设计是非常理想的。第二十页,共二十八页,编辑于2023年,星期五【例9.10】抽象类实例#include<iostream>usingnamespacestd;classCShape{//定义为一个抽象类,即一个图形界面接口public:virtualdoublearea()const{return0.0;}//定义为虚函数,允许后面覆盖

virtualvoidprintShapeName()const=0;//定义为纯虚函数,由派生类负责实现

virtualvoiddraw()const=0;//定义为纯虚函数};第二十一页,共二十八页,编辑于2023年,星期五classCPoint:publicCShape//公有继承CShape{public:CPoint(int=0,int=0);//声明构造函数

voidsetPoint(int,int);intgetX()const{returnx;}intgetY()const{returny;}virtualvoidprintShapeName()const//覆盖CShape基类的纯虚函数

{cout<<"Point:";}virtualvoiddraw()const;private:intx,y;};第二十二页,共二十八页,编辑于2023年,星期五CPoint::CPoint(inta,intb)//CPoint构造函数的实现{setPoint(a,b);}voidCPoint::setPoint(inta,intb){x=a;y=b;}voidCPoint::draw()const{cout<<"["<<x<<","<<y<<"]";}第二十三页,共二十八页,编辑于2023年,星期五classCCircle:publicCPoint{public:CCircle(doubler=0.0,intx=0,inty=0);voidsetRadius(double);doublegetRadius()const;virtualdoublearea()const;virtualvoidprintShapeName()const{cout<<"Circle:";}virtualvoiddraw()const;private:doubleradius;};第二十四页,共二十八页,编辑于2023年,星期五CCircle::CCircle(doubler,inta,intb):CPoint(a,b){setRadius(r);}voidCCircle::setRadius(doubler){radius=r>0?r:0;}doubleCCircle::getRadius()const{returnradius;}doubleCCircle::area()const{return3.1415926*radius*radius;}voidCCircle::draw()const{CPoint::draw();cout<<";Radius="<<radius;}第二十五页,共二十八页,编辑于2023年,星期五classCRectangle:publicCPoint{public:CRectangle(doublewidth=0.0,doubleheight=0.0,intx=0,inty=0);voidsetWidth(double);doublegetWidth();voidsetHeight(double);doublegetHeight();virtualdoublearea()const;virtualvoidprintShapeName()const{cout<<"Rectangle:";}virtualvoiddraw()const;private:doublewidth;doubleheight;};第二十六页,共二十八页,编辑于2023年,星期五CRectangle::CRectangle(doublew,doubleh,intx,inty):CPoint(x,y){setWidth(

温馨提示

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

评论

0/150

提交评论