资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,09 十一月 2026,第9章 多态性和虚函数,9.1 多态性的概念,多态性是指不同类的对象对于同一消息的处理具有不同的实现。,多态性在C+中表现为同一形式的函数调用,可能调用不同的函数实现。从系统实现的角度看,C+的多态性分为两类,一类称为,编译时刻多态性,,另一类称为,运行时刻多态性,,也称动态多态性。,9.1.1 编译时刻的多态性,C+编译时多态性通过重载(函数重载和运算符重载)来实现,【例9.1】编译时刻的多态性运算符重载:下面这段程序建立Rectangle类和Cuboid类,并重载运算符“+=”,使之能用于相应类对象的运算。,#include,using namespace std;,class Rectangle /定义矩形类,public:,Rectangle(double w=0,double l=0);/缺省构造函数,void set_wl(double w,double l);,double get_w()const;,double get_l()const;,double area();,Rectangle();/析构函数,Rectangle&operator+=(Rectangle&rec_add)/重载运算符+=,width+=rec_add.width;,length+=rec_add.length;,return*this;/返回当前对象,protected:,int width;,int length;,;,Rectangle:Rectangle(double w,double l):width(w),length(l),void Rectangle:set_wl(double w,double l),width=w;length=l;,double Rectangle:get_w()const,return width;,double Rectangle:get_l()const,return length;,double Rectangle:area(),return width*length;,class Cuboid:public Rectangle /定义长方体类,public:,Cuboid(double w=0,double l=0,double h=0);,void set_wlh(double w,double l,double h);,double get_h()const;,double area();,Cuboid&operator+=(Cuboid&cub_add),width+=cub_add.width;,length+=cub_add.length;,height+=cub_add.height;,return*this;/返回当前对象,protected:,double height;,;,Cuboid:Cuboid(double w,double l,double h):Rectangle(w,l),height(h),void Cuboid:set_wlh(double w,double l,double h),width=w;length=l;height=h;,double Cuboid:get_h()const,return height;,double Cuboid:area(),return 2*(width*length+width*height+length*height);/求长方体的表面积,int main(),Rectangle rec1(1,2);,Rectangle rec2;,Cuboid cub1(1,2,3);,Cuboid cub2;,rec2.set_wl(2,4);,cub2.set_wlh(5,10,15);,coutrec1:(width=rec1.get_w(),length=rec1.get_l()endl;,coutrec2:(width=rec2.get_w(),length=rec2.get_l()endl;,rec2+=rec1;/调用Rectangle类的重载运算符:+=,coutrec2.width=(rec2.get_w(),length=rec2.get_l()endl;,coutcub1:(width=cub1.get_w(),length=cub1.get_l(),height=;,coutcub1.get_h()endl;,coutcub2:(width=cub2.get_w(),length=cub2.get_l(),height=;,coutcub2.get_h()endl;,cub2+=cub1;/调用Cuboid类的重载运算符:+=,coutcub2:(width=cub2.get_w(),length=cub2.get_l(),height=;,coutcub2.get_h()endl;,coutrec2s area is rec2.area()endl;,coutcub2s area is cub2.area()endl;,return 0;,9.1.2 运行时刻的多态性,运行时刻多态性的实现是指在程序运行过程中根据具体情况来确定调用的是哪一个函数,它是通过动态联编机制实现的,【例9.2】运行时刻的多态性,运行时的多态性。仍然用例9.1中定义的Rectangle类和Cuboid类。/*ex9_2.cpp*,/此处加上例9.1中定义的Rectangle类和Cuboid类。,int main(),Rectangle*r;,Cuboid cub3(1,2,3);,coutcub3=(cub3.get_w(),cub3.get_l(),;,coutcub3.get_h()endl;,r=/用基类指针指向派生类对象,coutarea()endl;,coutcub3s area is(cub3)cub3.area()endl;,return 0;,程序的运行结果如下:,cub3=(1,2,3),cub3s area is(*r)2,cub3s area is(cub3)22,运行时刻的多态性是面向对象的一个非常重要的特征,再来看一个在结构化编程中的例子:,【例9.3】下面这段程序是利用多分支结构编程模拟实现绘制图形的函数。,/*ex9_3.cpp*,void draw(int obj_figure),switch(obj_figure),case 0:/rectangle,draw_rectangle();/cout”draw rectangle”endl;,break;,case 1:/triangle,draw_triangle();/cout”draw triangle”endl;,break;,case 2:/circle,draw_circle();/coutdraw_circleendl;,break;,这种编程方式使得程序的可维护性和可扩充性都变得很差。那么有没有更好的方法实现上述,例子?看看下面的程序段:,void draw(void*f),(*f)();,9.2 虚函数,虚函数的作用,虚函数从表现形式看是指那些被virtual关键字修饰的成员函数。类的一个成员函数如果被说明为虚函数,表明它目前的具体实现仅是一种适用于当前类的实现,而在该类的继承层次链条中有可能重新定义这个成员函数的实现,即这个虚函数可能会被派生类的同名函数所覆盖(override)。,例:使用虚函数的例子,#include,using std:cout;,using std:endl;,class figure,public:,virtual,void draw()/将draw()定义为虚函数,coutdraw figureendl;,;,class rectangle:public figure,void draw(),coutdraw rectangleendl;,;,class triangle:public figure,void draw(),coutdraw triangledraw();/调用r1的成员函数draw(),f=/基类指针f指向派生类对象t1,f-draw();/调用t1的成员函数draw(),return 0;,程序运行结果:,虚函数的使用,虚函数的实现机制和调用方式与非虚函数不同,虚函数的使用需要注意以下几点:,1虚函数的声明,只能将类的成员函数声明为虚函数,而不能将类外的普通函数声明为虚函数。虚函数的作用是允许在派生类中对基类的虚函数重新定义,因而它只能用于类的继承层次结构中。,2虚函数的访问权限,派生类中虚函数的访问权限并不影响虚函数的动态联编,如下面的例9.5,其中派生类CDerived中重新定义了虚函数F4(),在程序的运行中由于虚函数的机制,在CBase:F3()中调用F4()时会调用CDerived:F4(),而该函数的访问权限是私有的。,3成员函数中调用虚函数,在类的成员函数中可以直接调用相应类中定义或重新定义的虚函数,分析这类函数的调用次序时要注意成员函数的调用一般是隐式调用,应该将其看成是通过this指针的显式调用。,【例9.5】在成员函数中调用虚函数,/*ex9_5.cpp*,#include,using namespace std;,class CBase,public:,void F1(),coutCBase-F1=;,F2();,void F2(),cout;,F3();,virtual void F3(),cout;,F4();/即this-F4(),virtual void F4(),cout;,;,class CDerived:public CBase,private:,virtual void F4(),coutout;,CBase:F2();,void F2(),coutDerived-F2=;,F3();/即this-F3(),;,int main(),CBase*pB;,CDerived Obj;程序运行结果:,pB=,pB-F1();,Obj.F1();,return 0;,9.3 纯虚函数与抽象类,纯虚函数,在程序设计中,通常会在类层次的顶层以虚函数的形式给出该类层次所提供的某些操作的统一接口,由于层次较高,有些操作无法(也无必要)给出具体的实现,对于这种情况可以不对虚函数的实现进行定义,而将它们说明为纯虚函数。纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是:,virtual (参数表列)=0;,抽象类,具有纯虚函数的类无法用于创建对象,因为它的纯虚函数无函数体,所以又把这种含有纯虚函数的类称为抽象类。抽象类的主要作用是为一个族类提供统一的公共接口,用户在这个基础上根据自己的需要定义出功能各异的派生类,以有效地发挥多态的特性。,使用抽象类时应注意以下问题:,抽象类只能用作其它类的基类,不能建立抽象类的对象。因为它的纯虚函数没有定义功能。,抽象类不能用作参数类型、函数的返回类型或显式转换的类型。,可以声明抽象类的指针和引用,通过它们,可以指向并访问派生类对象,从而访问派生类的成员。,如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,那么这些函数就被赋予了功能,可以被调用。这个派生类就不是抽象类,而是可以用来定义对象的具体类。如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。,9.4 抽象类的实例,一个抽象类就是一个界面。类层次结构是一种逐步递增地建立类的方式。有些抽象类也提供了重要的功能,支持进一步向上构造。类层次结构中的各个类一方面为用户提供了有用的功能,同时也作为实现更高级或者更特殊的类的构造块。这种层次结构对于支持以逐步求精方式进行的程序设计是非常理想的。,【例9.10】抽象类实例,#include,using namespace std;,class CShape /定义为一个抽象类,即一个图形界面接口,public:,virtual double area()const return 0.0;/定义为虚函数,允许后面覆盖,virtual void printShapeName()const=0;/定义为纯虚函数,由派生类负责实现,virtual void draw()const=0;/定义为纯虚函数,;,class CPoint:public CShape /公有继承CShape,public:,CPoint(int=0,int=0);/声明构造函数,void setPoint(int,int);,int getX()const,return x;,int getY()const,return y;,virtual void printShapeName()const /覆盖CShape基类的纯虚函数,coutPoint:;,virtual void draw()const;,private:,int x,y;,;,CPoint:CPoint(int a,int b)/CPoint构造函数的实现,setPoint(a,b);,void CPoint:setPoint(int a,int b),x=a;,y=b;,void CPoint:draw()const,coutx,y;,class CCircle:public CPoint,public:,CCircle(double r=0.0,int x=0,int y=0);,void setRadius(double);,double getRadius()const;,virtual double area()const;,virtual void printShapeName()const,cout0?r:0;,double CCircle:getRadius()const,return radius;,double CCircle:area()const,return 3.1415926*radius*radius;,void CCircle:draw()const,CPoint:draw();,cout;Radius=radius;,class CRectangle:public CPoint,public:,CRectangle(double width=0.0,double height=0.0,int x=0,int y=0);,void setWidth(double);,double getWidth();,void setHeight(double);,double getHeight();,virtual double area()const;,virtual void printShapeName()const,cout0?w:0;,void CRectangle:setHeight(double h),height=h0?h:0;,double CRectangle:getWidth(),return width;,double CRectangle:getHeight(),return height;,double CRectangle:area()const,return width*height;,void CRectangle:draw()const,CPoint:draw();,cout width=width;Height=height;,void ViaPointer(const CShape*);,void ViaReference(const CShape,int main(),CPoint point(5,9);/定义point对象并初始化,CCircle circle(4.5,14,8);/定义circle对象并初始化,CRectangle rectangle(12,3.5,8,9);/定义rectangle对象并初始化,point.printShapeName();/静态绑定,point.draw();/静态绑定,coutendl;,circle.printShapeName();/静态绑定,circle.draw();/静态绑定,coutendl;,rectangle.printShapeName();/静态绑定,rectangle.draw();/静态绑定,coutendl;,CShape*Shapes3;,Shapes0=/指向point对象的基类指针,Shapes1=/指向circle对象的基类指针,Shapes2=/指向rectangle对象的基类指针,coutVirtual function calls made off base-class pointersendl;,for(int i=0;i3;i+),ViaPointer(Shapesi);/指针作为实参,coutVirtual function calls made off base-class referencesendl;,for(int j=0;jprintShapeName();/动态绑定,即运行时多态,basePtr-draw();/动态绑定,coutarea()endl;,void ViaReference(const CShape&baseClassRef)/基类对象的引用,baseClassRef.printShapeName();/动态绑定,baseClassRef.draw();/动态绑定,cout;Area=baseClassRef.area()endl;,
展开阅读全文