谭浩强C程序设计 nPPT课件

上传人:英*** 文档编号:78723557 上传时间:2022-04-22 格式:PPTX 页数:55 大小:179.52KB
返回 下载 相关 举报
谭浩强C程序设计 nPPT课件_第1页
第1页 / 共55页
谭浩强C程序设计 nPPT课件_第2页
第2页 / 共55页
谭浩强C程序设计 nPPT课件_第3页
第3页 / 共55页
点击查看更多>>
资源描述
多态性(polymorphism)是面向对象程序设计的一个重要特征。利用多态性可以设计和实现一个易于扩展的系统。p在C+程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。p在面向对象方法中的多态性: 向不同的对象发送同一个消息(函数名),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。12.1 多态性的概念第1页/共55页优点:在C+程序设计中,在不同的类中定义了其响应消息的方法,那么使用这些类时,不必考虑它们是什么类型,只要发布消息即可。多态性分为两类: 静态多态性和动态多态性。p静态多态性:函数重载和运算符重载属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,因此静态多态性又称编译时的多态性。p动态多态性:是在程序运行过程中才动态地确定操作所针对的对象。它又称运行时的多态性。动态多态性是通过虚函数(virtual function)实现的。在本章中主要介绍动态多态性和虚函数。第2页/共55页下面是一个承上启下的例子。一方面它是有关继承和运算符重载内容的综合应用的例子,通过这个例子可以进一步融会贯通前面所学的内容,另一方面又是作为讨论多态性的一个基础用例。12.2 一个典型的例子第3页/共55页例12.1 先建立一个Point(点)类,包含数据成员x,y(坐标点)。以它为基类,派生出一个Circle(圆)类,增加数据成员r(半径),再以Circle类为直接基类,派生出一个Cylinder(圆柱体)类,再增加数据成员h(高)。要求编写程序,重载运算符“”,使之能用于输出以上类对象。对于一个比较大的程序,应当分成若干步骤进行。先声明基类,再声明派生类,逐级进行,分步调试。(1) 声明基类Point类 写出声明基类Point的部分:#include /声明类Point /声明一个类包括什么?class Pointpublic: Point(float x=0,float y=0);/有默认参数的构造函数第4页/共55页 void setPoint(float,float); /设置坐标值 float getX( ) const return x; /读x坐标 float getY( ) const return y; /读y坐标 friend ostream & operator(ostream &,const Point &);/重载运算符“”protected: /受保护成员 float x,y;/下面定义Point类的成员函数 /Point的构造函数Point:Point(float a,float b) /对x,y初始化x=a;y=b;/设置x和y的坐标值void Point:setPoint(float a,float b) /为x,y赋新值x=a;y=b;/重载运算符“”,使之能输出点的坐标ostream & operator(ostream &output,const Point &p)outputp.x,p.yendl; return output;以上完成了基类Point类的声明。第5页/共55页现在要对上面写的基类声明进行调试,检查它是否有错,为此要写出main函数。实际上它是一个测试程序。int main( )Point p(3.5,6.4);/建立Point类对象p coutx=p.getX( ),y=p.getY( )endl;/输出p的坐标值 p.setPoint(8.5,6.8); /重新设置p的坐标值 coutp(new):pendl; /用重载运算符“”输出p点坐标程序编译通过,运行结果为x=3.5,y=6.4p(new):8.5,6.8测试程序检查了基类中各函数的功能,以及运算符重载的作用,证明程序是正确的。第6页/共55页(2) 声明派生类Circleclass Circle:public Point/circle是Point类的公用派生类public: Circle(float x=0,float y=0,float r=0); /构造函数 void setRadius(float); /设置半径值 float getRadius( ) const; /读取半径值 float area ( ) const; /计算圆面积 friend ostream &operator(ostream &,const Circle &);/重载运算符“” private:/?protected: float radius;/定义构造函数,对圆心坐标和半径初始化Circle:Circle(float a,float b,float r):Point(a,b),radius(r) void Circle:setRadius(float r) /设置半径值radius=r;float Circle:getRadius( ) const return radius; /读取半径值float Circle:area( ) const第7页/共55页return 3.14159*radius*radius;/重载运算符“”,使之按规定的形式输出圆的信息ostream &operator(ostream &output,const Circle &c)outputCenter=c.x,c.y,r=c.radius,area=c.area( )endl; return output;为了测试Circle类的定义,可以写出下面的主函数:第8页/共55页i n t m a i n ( ) C i r c l e c ( 3 . 5 , 6 . 4 , 5 . 2 ) ; / / 建 立 C i r c l e 类 对 象 c , 并 给 定 圆 心 坐 标 和 半 径 c o u t o r i g i n a l c i r c l e : n x = c . g e t X ( ) , y = c . g e t Y ( ) , r = c . g e t R a d i u s ( ) , a r e a = c . a r e a ( ) e n d l ; / / 输 出 圆 心 坐 标 、 半 径 和 面 积 c . s e t R a d i u s ( 7 . 5 ) ; / / 设 置 半 径 值 c . s e t P o i n t ( 5 , 5 ) ; / / 设 置 圆 心 坐 标 值 x , y c o u t n e w c i r c l e : n c ; / / 用 重 载 运 算 符 “ ” 输 出 圆 对 象 的 信 息 P o i n t & p R e f = c ; / / p R e f 是 P o i n t 类 的 引 用 变 量 , 被 c 初 始 化 c o u t p R e f : p R e f ; / / 输 出 p R e f 的 信 息 r e t u r n 0 ;程 序 编 译 通 过 , 运 行 结 果 为 o r i g i n a l c i r c l e : ( 输 出 原 来 的 圆 的 数 据 )x = 3 . 5 , y = 6 . 4 , r = 5 . 2 , a r e a = 8 4 . 9 4 8 6n e w c i r c l e : ( 输 出 修 改 后 的 圆 的 数 据 )C e n t e r = 5 , 5 , r = 7 . 5 , a r e a = 1 7 6 . 7 1 4p R e f : 5 , 5 ( 输 出 圆 的 圆 心 “ 点 ” 的 数 据 )第9页/共55页(3) 声明Circle的派生类Cylinderclass Cylinder:public Circle/ Cylinder是Circle的公用派生类public: Cylinder (float x=0,float y=0,float r=0,float h=0);/构造函数 void setHeight(float); /设置圆柱高 float getHeight( ) const; /读取圆柱高 float area( ) const; /计算圆表面积 float volume( ) const; /计算圆柱体积 friend ostream& operator(ostream&,const Cylinder&);/重载运算符“” protected: float height; /圆柱高;Cylinder:Cylinder(float a,float b,float r,float h) /定义构造函数 :Circle(a,b,r),height(h)void Cylinder:setHeight(float h)height=h; /设置圆柱高float Cylinder:getHeight( ) const return height; /读取圆柱高第10页/共55页/计算圆表面积float Cylinder:area( ) const return 2*Circle:area( )+2*3.14159*radius*height;float Cylinder:volume() const/计算圆柱体积return Circle:area()*height;/重载运算符“”ostream &operator(ostream &output,const Cylinder& cy)outputCenter=cy.x,cy.y,r=cy.radius,h=cy.height narea=cy.area( ), volume=cy.volume( )endl; return output;第11页/共55页 可以写出下面的主函数: int main( )Cylinder cy1(3.5,6.4,5.2,10);/定义Cylinder类对象cy1 coutn original cylinder:nx=cy1.getX( ), y=cy1.getY( ), r= cy1.getRadius( ), h=cy1.getHeight( )narea=cy1.area() ,volume=cy1.volume()endl;/用系统定义的运算符“”输出cy1的数据 cy1.setHeight(15); /设置圆柱高 cy1.setRadius(7.5); /设置圆半径 cy1.setPoint(5,5); /设置圆心坐标值x,y coutnnew cylinder:ncy1; /用重载运算符“”输出cy1的数据 Point &pRef=cy1; /pRef是Point类对象的引用变量第12页/共55页 coutnpRef as a Point:pRef; /pRef作为一个“点”输出 Circle &cRef=cy1; /cRef是Circle类对象的引用变量 coutncRef as a Circle:display( );”可以调用不同派生层次中的display函数,只需在调用前给指针变量pt赋以不同的值(使之指向不同的类对象)即可。C+中的虚函数就是用来解决这个问题的。虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。方法:由基类指针,访问各层次中的同名函数。例12.2。再讨论使用虚函数的情况。第15页/共55页例12.2 基类与派生类中有同名函数。在下面的程序中Student是基类,Graduate是派生类,它们都有display这个同名的函数。#include #include using namespace std;/声明基类Studentclass Studentpublic: Student(int, string,float);/声明构造函数 void display( ); /声明输出函数 protected: /受保护成员,派生类可以访问 int num; string name; float score; ;第16页/共55页 /Student类成员函数的实现Student:Student(int n, string nam,float s) /定义构造函数 num=n;name=nam;score=s;void Student:display( ) /定义输出函数coutnum:numnname:namenscore:scorenn;第17页/共55页/声明公用派生类Graduateclass Graduate:public Studentpublic: Graduate(int, string, float, float); /声明构造函数 void display( ); /声明输出函数private: float pay;/ Graduate类成员函数的实现void Graduate:display( ) /定义输出函数 coutnum:numnname:namenscore:scorenpay=paydisplay( ); pt=&grad1; pt-display( ); return 0; 第19页/共55页运行结果如下,请仔细分析。num:1001(stud1的数据)name:Liscore:87.5num:2001 (仅输出了grad1中基类部分的数据,没实现多态性!)name:wangscore:98.5下面对程序作一点修改,在Student类中声明display函数时,在最左面加一个关键字virtual,即virtual void display( );变成虚函数就行了这样就把Student类的display函数声明为虚函数。程序其他部分都不改动。再编译和运行程序,请注意分析运行结果: 第20页/共55页num:1001(stud1的数据)name:Liscore:87.5num:2001 (grad1中基类部分的数据,达到目的!)name:wangscore:98.5pay=1200 (这一项以前是没有的)由虚函数实现的动态多态性就是: 同一类族中不同类的对象,对同一函数调用作出不同的响应。多态性理解:起始地址相同的不同对象指针,调同名函数时,响应不同(调了各自对应的函数)。实现方法:将同名函数声明为虚函数。第21页/共55页虚函数的使用方法是: 注意规则! (1) 在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。 在类外定义虚函数时,不必再加virtual。(2) 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。 第22页/共55页C+规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此在派生类重新声明该虚函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰。如果在派生类中没有对基类的虚函数重新定义,则派生类简单地继承其直接基类的虚函数。(3) 必须定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。(4) 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。第23页/共55页举例2 :用引用也可,如将上面例题改为void fun( Student & x)/必须为基类指针或引用! x.display( );/主函数int main() Student s1(1001,Li,87.5); Graduate gd1(2001,Wang,98.5,563.5); fun( s1); fun (gd1); return 0;第24页/共55页显示结果:num:1001(stud1的数据)name:Liscore:87.5num:2001 (grad1的数据)name:wangscore:98.5pay=1200第25页/共55页注意区别:函数重载处理的是同一层次上的同名函数问题,而虚函数处理的是不同派生层次上的同名函数问题,前者是横向重载,后者可以理解为纵向重载。但与重载不同的是: 同一类族的虚函数的首部是相同的,而函数重载时函数的首部是不同的(参数个数或类型不同)。第26页/共55页这样编译系统在对程序进行编译时,即能确定调用的是哪个类对象中的函数。确定调用的具体对象的过程称为关联(binding)。在这里是指把一个函数名与一个类对象捆绑在一起,建立关联。一般地说,关联指把一个标识符和一个存储地址联系起来。12.3.2 静态关联与动态关联 第27页/共55页函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类,其过程称为静态关联(static binding),由于是在运行前进行关联的,故又称为早期关联(early binding)。函数重载属静态关联。 而运行时的多态性,编译系统在编译该行时是无法确定调用哪一个类对象的虚函数的。因为编译只作静态的语法检查,光从语句形式是无法确定调用对象的。第28页/共55页例如,先使pt指向grad1,再执行“pt-display( )”,当然是调用grad1中的display函数。由于是在运行阶段把虚函数和类对象“绑定”在一起的,因此,此过程称为动态关联(dynamic binding)。这种多态性是动态的多态性,即运行阶段的多态性。在运行阶段,指针可以先后指向不同的类对象,从而调用同一类族中不同类的虚函数。由于动态关联是在编译以后的运行阶段进行的,因此也称为滞后关联(late binding)。第29页/共55页使用虚函数时,有两点要注意: (1)只能用virtual声明类的成员函数,使它成为虚函数,而不能将类外的普通函数声明为虚函数。因为虚函数的作用:是允许在派生类中对基类的虚函数重新定义。显然,它只能用于类的继承层次结构中。12.3.3 在什么情况下应当声明虚函数第30页/共55页(2) 应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问的,则应当声明为虚函数。需要说明的是: 使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table,简称vtable),它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,因此,多态性是高效的。第31页/共55页多态性进一步理解:多态性是指在运行时,能根据其类型确认调用哪个函数的能力。只支持类而不支持多态,称基于对象的;如VB。只有支持多态,才成为面向对象的。好处分析:如计算学生的学费,有大学生,研究生,博士生等。从程序设计看,用采取继承方式设计。希望只有一个收费员(函数)可以收各种学生的费用。 而不是首先有一个管理者判断是什么学生?再分派给各个类型的收费员(函数)去收费。 希望只有一个收费员void fn(Student &x) x.calcTuition();若不这样, fn中要判断,分别调用,维护量大,面向对象优越性被限制,又回到面向过程。 第32页/共55页析构函数的作用是在对象撤销之前做必要的“清理现场”的工作。当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用基类的析构函数。但用delete 删除对象时存在如下问题,即没有达到期望的目标。如何解决?12.3.4 虚析构函数(还有什么函数可以虚之?)(还有什么函数可以虚之?)第33页/共55页例12.3 基类中有非虚析构函数时的执行情况。#include using namespace std;class Point/定义基类Point类public: Point( ) /Point类构造函数 Point()coutexecuting Point destructorendl;/Point类析构函数;class Circle:public Point /定义派生类Circle类public: Circle( ) /Circle类构造函数 Circle( )coutexecuting Circle destructorendl;/Circle类析构函数 private: int radius;第34页/共55页int main( ) Point *p=new Circle; /用new开辟动态存储空间delete p; /用delete释放动态存储空间return 0;这只是一个示意的程序。p是指向基类的指针变量,指向new开辟的动态存储空间,希望用detele释放p所指向的空间,即元类对象的空间。但运行结果为executing Point destructor表示只执行了基类Point的析构函数,而没有执行派生类Circle的析构函数。第35页/共55页原因是:如果希望能执行派生类Circle的析构函数,方法:可以将基类的析构函数声明为虚析构函数,如virtual Point()coutexecuting Point destructorendl;程序其他部分不改动,再运行程序,结果为executing Circle destructorexecuting Point destructor先调用了派生类的析构函数,再调用了基类的析构函数,符合人们的愿望。第36页/共55页特点与好处:如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数。这样,如果程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,则系统会调用相应类的析构函数。一般即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,以保证在撤销动态分配空间时能得到正确的处理。构造函数不能声明为虚函数。这是因为在执行构造函数时类对象还未完成建立过程,当然谈不上函数与类对象的绑定。第37页/共55页例如在本章的例12.1程序中,基类Point中没有求面积的area函数,因为“点”是没有面积的,也就是说,基类本身不需要这个函数,所以在例12.1程序中的Point类中没有定义area函数。但是,在其直接派生类Circle和间接派生类Cylinder中都需要有area函数,而且这两个area函数的功能不同,一个是求圆面积,一个是求圆柱体表面积。仅供派生而无实际意义的函数,故纯虚之。12.4 纯虚函数与抽象类 12.4.1 纯虚函数第38页/共55页有的读者自然会想到,在这种情况下应当将area声明为虚函数。可以在基类Point中加一个area函数,并声明为虚函数: virtual float area( ) const return 0;其返回值为0,表示“点”是没有面积的。为简化,可以不写出这种无意义的函数体,只给出函数的原型,并在后面加上“=0”,如virtual float area( ) const =0;/纯虚函数这就将area声明为一个纯虚函数(pure virtual function)。纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是virtual 函数类型 函数名 (参数表列) =0;第39页/共55页注意: 纯虚函数没有函数体;最后面的“=0”并不表示函数返回值为0,告诉编译系统“这是纯虚函数”; 这是一个声明语句,最后应有分号。纯虚函数的作用:纯虚函数只有函数的名字而不具备函数的功能。它只是通知编译系统: “在这里声明一个虚函数,留待派生类中定义”。纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果在基类中没有保留函数名字,则无法实现多态性。如果在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数。第40页/共55页如果声明了一个类,一般可以用它定义对象。但是在面向对象程序设计中,往往有一些类,它们不用来生成对象。定义这些类的惟一目的是用它作为基类去建立派生类。这种不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract class),由于它常用作基类,通常称为抽象基类(abstract base class)。12.4.2 抽象类第41页/共55页凡是包含纯虚函数的类都是抽象类。因为纯虚函数是不能被调用的,包含纯虚函数的类是无法建立对象的。抽象类的作用是作为一个类族的共同基类,或者说,为一个类族提供一个公共接口。抽象类的条件:包含纯虚函数使用规则:如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,那么这些函数就被赋予了功能,可以被调用。这个派生类就不是抽象类,而是可以用来定义对象的具体类(concrete class)。如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。第42页/共55页虽然抽象类不能定义对象(或者说抽象类不能实例化),但是可以定义指向抽象类数据的指针变量。当派生类成为具体类之后,就可以用这种指针指向派生类对象,然后通过该指针调用虚函数,实现多态性的操作。总结:抽象类的实际作用:定义指向抽象类的指针,实现多态性操作。即对各纯虚函数可实现多态性操作。第43页/共55页例12.4 虚函数和抽象基类的应用。在本章例12.1介绍了以Point为基类的点圆圆柱体类的层次结构。现在要对它进行改写,在程序中使用虚函数和抽象基类。类的层次结构的顶层是抽象基类Shape(形状)。Point(点), Circle(圆), Cylinder(圆柱体)都是Shape类的直接派生类和间接派生类。下面是一个完整的程序,为了便于阅读,分段插入了一些文字说明。程序如下: 12.4.3 应用实例第44页/共55页第(1)部分#include using namespace std;/ /声明抽象基类Shape 因含纯虚函数!class Shape /目的:可使各纯虚函数实现多态性public: virtual float area( ) const return 0.0;/虚函数 virtual float volume() const return 0.0; /虚函数 virtual void shapeName() const =0; /纯虚函数; /第(2)部分/声明Point类class Point:public Shape/Point是Shape的公用派生类public: Point(float=0,float=0); void setPoint(float,float); float getX( ) const return x; float getY( ) const return y; virtual void shapeName( ) const coutPoint:; /对虚函数进行再定义 friend ostream & operator(ostream &,const Point &);protected: float x,y;第45页/共55页 ; Point:Point(float a,float b)x=a;y=b;void Point:setPoint(float a,float b)x=a;y=b;ostream & operator(ostream &output,const Point &p)outputp.x,p.y;return output;/第(3)部分/声明Circle类class Circle:public Pointpublic: Circle(float x=0,float y=0,float r=0); void setRadius(float); float getRadius( ) const; virtual float area( ) const; virtual void shapeName( ) const coutCircle:;/对虚函数进行再定义 friend ostream &operator(ostream &,const Circle &);protected:第46页/共55页 float radius;/声明Circle类成员函数Circle:Circle(float a,float b,float r):Point(a,b),radius(r) void Circle:setRadius(float r):radius(r) float Circle:getRadius( ) const return radius;float Circle:area( ) const return 3.14159*radius*radius;ostream &operator(ostream &output,const Circle &c)outputc.x,c.y, r=c.radius; return output;第(4)部分/声明Cylinder类class Cylinder:public Circlepublic: Cylinder (float x=0,float y=0,float r=0,float h=0); void setHeight(float); virtual float area( ) const; virtual float volume( ) const;第47页/共55页 virtual void shapeName( ) const coutCylinder:;/对虚函数进行再定义 friend ostream& operator(ostream&,const Cylinder&); protected: float height; /定义Cylinder类成员函数Cylinder:Cylinder(float a,float b,float r,float h) :Circle(a,b,r),height(h) void Cylinder:setHeight(float h)height=h;float Cylinder:area( ) const return 2*Circle:area( )+2*3.14159*radius*height;float Cylinder:volume( ) constreturn Circle:area( )*height;ostream &operator(ostream &output,const Cylinder& cy)outputcy.x,cy.y, r=cy.radius, h=cy.height;return output;第48页/共55页/main函数int main( )Point point(3.2,4.5); /建立Point类对象point Circle circle(2.4,1.2,5.6); /建立Circle类对象circle Cylinder cylinder(3.5,6.4,5.2,10.5); /建立Cylinder类对象cylinder point.shapeName(); /静态关联 coutpointendl; circle.shapeName(); /静态关联 coutcircleendl; cylinder.shapeName(); /静态关联 coutcylinderendlshapeName( ); /动态关联 coutx=point.getX( ),y=point.getY( )narea=area( ) nvolume=volume()shapeName( ); /动态关联 coutx=circle.getX( ),y=circle.getY( )narea=area( ) nvolume=volume( )shapeName( ); /动态关联 coutx=cylinder.getX( ),y=cylinder.getY( )narea=area( )nvolume=volume( )nn; return 0;以上程序也可:先定义函数 void fn( Shape & x) x. shapeName( ); 再 void main( ) fn(point); fn(circle); fn(cylinder); .程序运行结果如下:第50页/共55页P o i n t : 3 . 2 , 4 . 5 ( P o i n t 类 对 象 p o i n t 的 数 据 : 点 的 坐 标 )C i r c l e : 2 . 4 , 1 . 2 , r = 5 . 6 ( C i r c l e 类 对 象 c i r c l e 的 数 据 : 圆 心 和 半 径 )C y l i n d e r : 3 . 5 , 6 . 4 , r = 5 . 5 , h = 1 0 . 5 ( C y l i n d e r 类 对 象 c y l i n d e r 的 数 据 : 圆 心 、 半 径 和 高 )P o i n t : x = 3 . 2 , y = 4 . 5 ( 输 出 P o i n t 类 对 象 p o i n t 的 数 据 : 点 的 坐 标 )a r e a = 0 ( 点 的 面 积 )v o l u m e = 0 ( 点 的 体 积 )C i r c l e : x = 2 . 4 , y = 1 . 2 ( 输 出 C i r c l e 类 对 象 c i r c l e 的 数 据 : 圆 心 坐 标 )a r e a = 9 8 . 5 2 0 3 ( 圆 的 面 积 )v o l u m e = 0 ( 圆 的 体 积 )C y l i n d e r : x = 3 . 5 , y = 6 . 4 ( 输 出 C y l i n d e r 类 对 象 c y l i n d e r 的 数 据 : 圆 心 坐 标 )a r e a = 5 1 2 . 5 9 5 ( 圆 的 面 积 )v o l u m e = 8 9 1 . 9 6 ( 圆 柱 的 体 积 ) 第51页/共55页从本例可以进一步明确以下结论:(1) 一个基类如果包含一个或一个以上纯虚函数,就是抽象基类。抽象基类不能也不必要定义对象。(2) 抽象基类与普通基类不同,它不是现实存在的对象的抽象(例如圆形(Circle)就是千千万万个实际的圆的抽象),它可以没有任何物理上的或其他实际意义方面的含义。(3) 在类的层次结构中,顶层或最上面的几层可以是抽象基类。抽象基类体现了本类族中各类的共性,把各类中共有的成员函数集中在抽象基类中声明。(4) 抽象基类是本类族的公共接口。或者说,从同一基类派生出的多个类有同一接口。 (5) 区别静态关联和动态关联。第52页/共55页(6) 如果在基类声明了虚函数,则在派生类中凡是与该函数有相同的函数名、函数类型、参数个数和类型的函数,均为虚函数(不论在派生类中是否用virtual声明)。(7) 使用虚函数提高了程序的可扩充性。把类的声明与类的使用分离。这对于设计类库的软件开发商来说尤为重要。开发商设计了各种各样的类,但不向用户提供源代码,用户可以不知道类是怎样声明的,但是可以使用这些类来派生出自己的类。利用虚函数和多态性,程序员的注意力集中在处理普遍性,而让执行环境处理特殊性。第53页/共55页虚基类与抽象基类的区别:前者解决模糊二义性问题;后者解决继承中的多态性问题。实现时,前者由在继承方式前加virtual; 后者靠纯虚函数实现。虚函数与纯虚函数对比:纯在抽象类中,被“初始化”为0的函数。没有实际意义,为派生类保留名字,方便该函数实现多态性。虚函数有实际意义,也是为实现多态性服务。第54页/共55页感谢您的观看!第55页/共55页
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


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

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


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