收藏 分销(赏)

C++程序设计语言揣锦华 第8章 多态性.ppt

上传人:s4****5z 文档编号:10468659 上传时间:2025-05-29 格式:PPT 页数:87 大小:329.50KB
下载 相关 举报
C++程序设计语言揣锦华 第8章 多态性.ppt_第1页
第1页 / 共87页
C++程序设计语言揣锦华 第8章 多态性.ppt_第2页
第2页 / 共87页
点击查看更多>>
资源描述
,第二级,第三级,第四级,第五级,第8章 多态性,第8章 多态性,8.1 多态性概述,8.2 运算符重载,8.3 虚函数,8.4 抽象类,8.1 多态性概述,所谓多态性是指同一个接口可以通过多种方法调用,如图8-1所示。通俗地说,多态性是指用一个相同的名字定义不同的函数,这些函数的执行过程不同,但是有相似的操作,即用同样的接口访问不同的函数。比如,一个对象中有很多求两个数中最大值的行为,虽然可以针对不同的数据类型,写很多不同名称的函数来实现,但事实上,它们的功能几乎完全相同。这时,就可以利用多态的特征,用统一的标识来完成这些功能。,图8-1 多态性为用户提供单一接口示意图,面向对象的多态性从实现的角度来讲,可以分为静态多态性和动态多态性两种。静态多态性是在编译的过程中确定同名操作的具体操作对象的,而动态多态性则是在程序运行,过程中动态地确定操作所针对的具体对象的。这种确定操作具体对象的过程就是联编(,binding),,也称为绑定。联编是指计算机程序自身彼此关联的过程。也就是把一个标识符名和一个存储地址联系在一起的过程。用面向对象的术语讲,就是把一条消息和一个对象的方法相结合的过程。,所谓消息,是指对类的成员函数的调用。不同的方法是指不同的实现,也就是调用了不同的函数。按照联编进行阶段的不同,联编方法可以分为两种:静态联编和动态联编。这两种联编过程分别对应着多态的两种实现方式。联编工作在编译连接阶段完成的情况称为静态联编。在编译、连接过程中,系统就可以根据类型匹配等特征确定程序中操作调用与执行该操作的代码的关系,即确定某一个同名标识到底是要调用哪一段程序代码。函数重载和运算符重载就属于静态多态性。,和静态联编相对应,如果联编工作在程序运行阶段完成,则称为动态联编。在编译、连接过程中无法解决的联编问题,要等到程序开始运行之后再来确定。例如,本章将要介绍的虚函数就是通过动态联编完成的。,函数重载在函数及类的章节中曾做过详细的讨论,所以在本章中,静态多态性主要介绍运算符重载;对于动态多态性,将对虚函数作详细介绍。,8.2 运算符重载,C+,中预定义的运算符的操作对象只能是基本数据类型。实际上,对于很多用户自定义的类型(如类),也需要有类似的运算操作。例如,下面的程序声明了一个点类,point。,classpoint,/point,类声明,private:,intx,y;,public:/,构造函数,point(,intxx,=0,intyy,=0)x=,xx,;y=,yy,;,intget,_x();/,显示,x,值,intget,_y();/,显示,y,值,/.,;,于是我们可以这样声明点类的对象:,pointp1(1,1),p2(3,3),如果我们需要对,p1,和,p2,进行加法运算,该如何实现呢?我们当然希望能使用“+”运算符,写出表达式“,p1+p2”,,但是编译的时候却会出错,因为编译器不知道该如何完成这个加法。这时候,我们就需要自己编写程序来说明“+”在作用于,point,类对象时,该实现什么样的功能,这就是运算符重载。运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时,导致不同类型的行为。,在运算符重载的实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参,然后,根据实参的类型来确定需要调用的函数。这个过程是在编译过程中完成的。,8.2.1 运算符重载的规则,运算符是在,C+,系统内部定义的,它们具有特定的语法规则,如参数说明、运算顺序、优先级别等。因此,运算符重载时必须要遵守一定的规则。,C+,中的运算符除了少数几个(类属关系运算符“.”、作用域分辨符“:”、成员指针运算符“*”、,sizeof,运算符和三目运算符“?:”)之外,全部可以重载,而且只能重载,C+,中已有的运算符,不能臆造新的运算符。,重载之后运算符的优先级和结合性都不能改变,也不能改变运算符的语法结构,即单目运算符只能重载为单目运算符,双目运算符只能重载为双目运算符。,运算符重载后的功能应当与原有功能相类似。,重载运算符含义必须清楚,不能有二义性。,运算符的重载形式有两种:重载为类的成员函数和重载为类的友元函数。,运算符重载为类的成员函数的一般语法形式如下:,operator(形参表),函数体;,运算符重载为类的友元函数的一般语法形式如下:,friend,operator(形参表),函数体;,其中:,函数类型指定了重载运算符的返回值类型,也就是运算结果类型。,operator,是定义运算符重载函数的关键字。,运算符是要重载的运算符名称。,形参表给出重载运算符所需要的参数和类型。,friend,是对于运算符重载为友元函数时,在函数类型说明之前使用的关键字。,特别需要注意的是,当运算符重载为类的成员函数时,函数的参数个数比原来的操作数个数要少一个(后置“+”、“-”除外);当重载为类的友元函数时,参数个数与原操作数的个数相同。原因是重载为类的成员函数时,如果某个对象使用重载了的成员函数,自身的数据可以直接访问,就不需要再放在参数表中进行传递,少了的操作数就是该对象本身。,8.2.2 运算符重载为成员函数,运算符重载实质上就是函数重载,当运算符重载为成员函数之后,它就可以自由地访问本类的数据成员了。实际使用时,总是通过该类的某个对象来访问重载的运算符。如果是双目运算符,一个操作数是对象本身的数据,由,this,指针指出,另一个操作数则需要通过运算符重载函数的参数表来传递;如果是单目运算符,操作数由对象的,this,指针给出,就不再需要任何参数。下面分别介绍这两种情况。,1双目运算:,oprdlBoprd2,对于双目运算符,B,,如果要重载,B,为类的成员函数,使之能够实现表达式,oprdlBoprd2(,其中,oprdl,为,A,类的对象),则应当把,B,重载为,A,类的成员函数,该函数只有一个形参,形参的类型是,oprd2,所属的类型。经过重载之后,表达式,oprdlBoprd2,就相当于函数调用,oprdl,.,operatorB,(oprd2)。,2单目运算,1)前置单目运算:,Uoprd,对于前置单目运算符,U,,如“-”(负号)、“+”等,如果要重载,U,为类的成员函数,用来实现表达式,Uoprd,(,其中,oprd,为,A,类的对象),则,U,应当重载为,A,类的成员函数,函数没有形参。经过重载之后,表达式,Uoprd,相当于函数调用,oprd,.,operatorU,()。,例如,前置单目运算符“+”重载的语法形式如下:,operator+();,使用前置单目运算符“+”的语法形式如下:,+;,2)后置单目运算:,oprdV,再来看后置运算符,V,,如“+”和“-”,如果要将它们重载为类的成员函数,用来实现表达式,oprd,+,或,oprd,-(,其中,oprd,为,A,类的对象),那么运算符就应当重载为,A,类的成员函数,这时函数要带有一个整型(,int,),形参。重载之后,表达式,oprd,+,和,oprd,-,就相当于函数调用,oprd,.operator+(0),和,oprd,.operator-(0)。,例如,后置单目运算符“+”重载的语法形式如下:,operator+(,int,);,使用后置单目运算符“+”的语法形式如下:,+;,【例8-1】双目运算符重载为成员函数例题。,本例题重载二维点,point,加减法运算(关于二维点,point,类的定义在前面章节中已介绍过),将一个双目运算符重载为成员函数。,point,的加减法是,x,和,y,分别相加减,运算符的两个操作数都是,point,类的对象,因此,可以把“+”、“-”运算符重载为,point,类的成员函数,重载函数只有一个形参,类型同样也是,point,类对象。,#,include,classpoint,private:,floatx,y;,public:,point(,floatxx,=0,floatyy,=0)x=,xx,;y=,yy,;,floatget,_x(),returnx,;,floatget,_y(),returny,;,pointoperator,+(pointp1);/,重载运算符“+”,pointoperator,-(pointp1);/,和“-”为成员函数,;,pointpoint,:operator+(,pointq,),returnpoint,(x+q.x,y+q.y);,pointpoint,:operator-(,pointq,),returnpoint,(x-q.x,y-q.y);,voidmain,(),pointp1(3,3),p2(2,2),p3,p4;/,声明,point,类的对象,p3=p1+p2;/,两点相加,p4=p1-p2;/,两点相减,cout,p1+p2:x=p3.get_x(),y=p3.get_y(),endl,;,cout,p1-p2:x=p4.get_x(),y=p4.get_y(),endl,;,在本例中,将,point,的加减法运算重载为,point,类的成员函数。可以看出,除了在函数声明及实现的时候使用了关键字,operator,之外,运算符重载成员函数与类的普通成员函数没有什么区别。在使用的时候,可以直接通过运算符、操作数的方式来完成函数调用。这时,运算符“+”、“-”原有的功能都不改变,对整型数、浮点数等基本类型数据的运算仍然遵循,C+,预定义的规则,同时添加了新的针对,point,运算的功能。“+”这个运算符,作用于不同的对象就会导致完全不同的操作行为,具有了更广泛的多态特征。,本例中重载的“+”、“-”函数中,都是创建一个临时的无名对象作为返回值:,returnpoint,(x+q.x,y+q.y);,这表面上看起来像是对构造函数的调用,但其实并非如此。这是临时对象语法,它的含义是创建一个临时对象并返回它。当然,也可以按如下形式返回函数值:,pointpoint,:operator+(,pointq,),pointp,;,p.x=x+q.x;,p.y=y+q.y;,returnp,;,pointpoint,:operator-(,pointq,),pointp,;,p.x=x-q.x;,p.y=y-q.y;,returnp,;,这两种方法的执行效率是完全不同的。后者的执行过程是这样的:创建一个局部对象,p(,这时会调用构造函数),执行,return,语句时,会调用拷贝构造函数,将,p,的值拷贝到主调函数中的一个无名临时对象中。当函数,operator+,结束时,会调用析构函数析构对象,p,,然后,p,消亡。两种方法相比,前一种方法的效率高,因为它是直接将一个无名临时对象创建到主调函数中。,例8-1的程序运行结果为,p1+p2:x=5,y=5,p1-p2:x=1,y=1,【例8-2】单目运算符重载为成员函数例题。,本程序为时钟计时程序。在程序中将单目运算符重载为类的成员函数,单目运算符前置“+”和后置“+”的操作数是时钟类的对象,可以把这些运算符重载为时钟类的成员函数。对于前置单目运算符,重载函数没有形参;对于后置单目运算符,重载函数有一个整数形参。本例中,我们把自增前置“+”和自减前置“-”运算重载为,point,类的成员函数。,#,include,classpoint,private:,floatx,y;,public:,point(,floatxx,=0,floatyy,=0)x=,xx,;y=,yy,;,floatget,_x(),returnx,;,floatget,_y(),returny,;,pointoperator,+();/,重载前置运算符“+”,pointoperator,-();/,重载前置运算符“-”,;,pointpoint,:operator+(),if(x640)+x;,if(y0)-x;,if(y0)-y;,return*this;,voidmain,(),pointp1(10,10),p2(200,200);/,声明,point,类的对象,for(,inti,=0;i5;i+),cout,p1:x=p1.get_x(),y=p1.get_y(),endl,;,+p1;,for(i=0;i5;i+),cout,p2:x=p2.get_x(),y=p2.get_y()”。,1双目运算:,oprdlBoprd2,对于双目运算符,B,,如果,oprdl,为,A,类的对象,则应当把,B,重载为,A,类的友元函数,该函数有两个形参,其中一个形参的类型是,A,类。经过重载之后,表达式,oprdlBoprd2,就相当于函数调用,operatorB,(,oprdl,,oprd2)。,2单目运算,1)前置单目运算:,Uoprd,对于前置单目运算符,U,,如“-”(负号)等,如果要实现表达式,Uoprd,(,其中,oprd,为,A,类的对象),则,U,可以重载为,A,类的友元函数,函数的形参为,A,类的对象。经过重载之后,表达式,Uoprd,相当于函数调用,operatorU,(,oprd,)。,2)后置单目运算:,oprdV,对于后置运算符,V,,如“+”和“-”,如果要实现表达式,oprd,+,或,oprd,-(,其中,oprd,为,A,类的对象),那么运算符就可以重载为,A,类的友元函数,这时函数的形参有两个,一个是,A,类的对象,oprd,,,另一个是整型(,int,),形参。重载之后,表达式,oprd,+,和,oprd,-,就相当于函数调用,operator+(,oprd,,0),和,operator-(,oprd,,0)。,【例8-3】双目运算符重载为友元重载例题。,本例题用运算符重载为友元函数的方法重做两点加减法运算。,#,include,classpoint,private:,floatx,y;,public:,point(,floatxx,=0,floatyy,=0)x=,xx,;y=,yy,;,floatget,_x(),returnx,;,floatget,_y(),returny,;,friendpointoperator,+(pointp1,pointp2);/,重载运算符“+”,friendpointoperator,-(pointp1,pointp2);/,和“-”为友元函数,;,pointoperator,+(pointp1,pointp2),returnpoint,(p1.x+p2.x,p1.y+p2.y);,pointoperator,-(pointp1,pointp2),returnpoint,(p1.x-p2.x,p1.y-p2.y);,voidmain,(),pointp1(3,3),p2(2,2),p3,p4;/,声明,point,类的对象,p3=p1+p2;/,两点相加,p4=p1-p2;/,两点相减,cout,p1+p2:x=p3.get_x(),y=p3.get_y(),endl,;,cout,p1-p2:x=p4.get_x(),y=p4.get_y(),endl,;,从上述程序可以看到,将运算符重载为类的友元函数时,必须把操作数全部通过形参的方式传递给运算符重载函数。和例8-1相比,本例题的主函数根本没有做任何改动,主要的变化在,point,类的成员,程序运行的结果完全相同。,8.2.4 其它运算符重载,前面介绍了一些简单运算符的重载,除此之外,还有以下运算符也常被重载。,比较运算符重载(如,=,=,!=)。,赋值运算符重载(如=,+=,-=,*=,/=)。,下标运算符“”重载。,下标运算符“”通常用于取数组中的某个元素,通过下标运算符重载,可以实现数组下标的越界检测等。,运算符,new,和,delete,重载。,通过重载,new,和,delete,,可以克服,new,和,delete,的不足,使其按要求完成对内存的管理。,逗号运算符“,”重载。,逗号运算符是一个双目运算符,和其它运算符一样,我们也可以通过重载逗号运算符来达到期望的结果。逗号运算符构成的表达式为“左操作数,右操作数”,该表达式返回右操作数的值。,8.3 虚函数,8.3.1为什么要引入虚函数,为什么要引入虚函数,我们来看一个例子。,【例8-4】没有使用虚函数的例题。,#,include,classbase,/,定义基类,base,public:,voidwho,(),cout,thisistheclassofbase,!,endl,;,;,classderive1:,publicbase,/,定义派生类,derive1,public:,voidwho,(),cout,thisistheclassofderive1!,endl,;,;,classderive2:,publicbase,/,定义派生类,derive2,public:,voidwho,(),cout,thisistheclassofderive2!who();,ptr,=,ptr,-who();,ptr,=,ptr,-who();,obj1.who();,obj2.who();,return1;,此例在,main(),函数中定义了一个基类对象,obj,,,和两个派生类对象,obj1,与,obj2,,又定义了一个指向基类对象的指针,ptr,。,此程序的意图是用,ptr,指针分别指向不同的对象,以便执行不同对象所对应的类的成员函数。当,ptr,指向,obj,对象时,,ptr,-who(),调用,base,类的成员函数,who();,当,ptr,指向,obj1,对象时,我们希望,ptr,-who(),调用,derive1,类的成员函数,who();,而当,ptr,指向,obj2,对象时,则希望,ptr,-who(),调用,derive2,类的成员函数,who()。,此程序执行后实际得到的结果为,thisistheclassofbase,!(a),thisistheclassofbase,!(b),thisistheclassofbase,!(c),thisistheclassofderive1!(d),thisistheclassofderive2!(e),在运行结果中,(,a)、(d),和(,e),与所预想的相符,而(,b),和(,c),却不是希望得到的。这说明,不管指针,ptr,当前指向哪个对象(是基类对象还是派生类对象),,ptr,-who(),调用的都是基类中定义的,who(),函数。也就是说,通过指针引起的普通成员函数调用,仅仅与指针的类型有关,而与指针正指向什么对象无关。在这种情况下,必须采用显式的方式调用派生类的函数成员。,例如:,obj1.who(),或,obj2.who(),或者是采用对指针的强制类型转换的方法,例如:,(,derive1*),ptr,)-who(),或(,derive2*),ptr,)-who(),本来使用对象指针是为了表达一种动态的性质,即当指针指向不同对象时执行不同的操作,现在看来并没有起到这种作用。要实现这种功能,就需要引入虚函数的概念。这里,只需将基类的,who(),函数声明为虚函数即可。,8.3.2 虚函数的定义及使用,1.虚函数的定义,虚函数的定义是在基类中进行的。它是在基类中需要定义为虚函数的成员函数的声明中冠以关键字,virtual。,当基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义,在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型以及参数的顺序都必须与基类中的原型完全相同。,一般虚函数的定义语法如下:,virtual(形参表),函数体,其中,被关键字,virtual,说明的函数为虚函数。特别要注意的是,虚函数的声明只能出现在类声明中的函数原型声明中,而不能出现在成员的函数体实现的时候。,需要注意,动态联编只能通过成员函数来调用或者通过指针、引用来访问虚函数。如果使用对象名的形式访问虚函数,则将采用静态联编方式调用虚函数,而无需在运行过程中进行调用。下面是通过指针访问虚函数的例题。,【例8-5】使用虚函数例题。,#,include,classbase,/,定义基类,base,public:,virtualvoidwho,()/,虚函数声明,cout,thisistheclassofbase,!,endl,;,;,classderive1:,publicbase,/,定义基类派生类,derive1,public:,voidwho,()/,重新定义虚函数,cout,thisistheclassofderive1!,endl,;,;,classderive2:,publicbase,/,定义派生类,derive2,public:,voidwho,()/,重新定义虚函数,cout,thisistheclassofderive2!who();,ptr,=,ptr,-who();,ptr,=,ptr,-who();,return1;,此时,程序运行结果为,thisistheclassofbase,!,thisistheclassofderive1!,thisistheclassofderive2!,分析一下上面这个程序,在基类中对,voidwho,(),进行了虚函数声明,这样,在其派生类中就可以重新定义它。在派生类,derive1,和,derive2,中分别重新定义,voidwho,(),函数,注意,此虚函数在派生类中重新定义时不再需要,virtual,声明,此声明只在其基类中出现一次。在,voidwho,(),函数被重新定义时,其函数的原型与基类中的函数原型必须完全相同。,在,main(),函数中,定义了一个指向基类类型的指针,它也被允许指向其派生类。在执行过程中,不断改变它所指向的对象,,ptr,-who(),就能调用不同的版本。虽然都是,ptr,-who(),语句,但是,当,ptr,指向不同的对象时,所对应的执行动作就不同。由此可见,用虚函数充分体现了多态性。并且,因为,ptr,指针指向哪个对象是在执行过程中确定的,所以体现的又是一种动态的多态性。,2.虚函数与重载的关系,在一个派生类中重新定义基类的虚函数是函数重载的另一种特殊形式,但它不同于一般的函数重载。,一般的函数重载,只要函数名相同即可,函数的返回类型及所带的参数可以不同。但当重载一个虚函数时,也就是说在派生类中重新定义此虚函数时,要求函数名、返回类型、参数个数、参数类型以及参数的顺序都与基类中的原型完全相同,不能有任何的不同。,3多继承中的虚函数,在多继承中由于派生类是由多个基类派生而来的,因此,虚函数的使用就不像单继承那样简单。请看下面的例题。,【例8-6】多继承中使用虚函数例题。,#,include,classbase1/,定义基类,base1,public:,virtualvoidwho,()/,函数,who(),为虚函数,cout,thisistheclassofbase1!,endl,;,;,classbase2/,定义基类,base2,public:,voidwho,()/,此函数,who(),为一般的函数,cout,thisistheclassofbase2!,endl,;,;,classderive,:publicbase1,publicbase2,public:,voidwho,(),cout,thisistheclassofderive,!who();,ptr2=,ptr2-who();,ptr1=,ptr1-who();,ptr2=,ptr2-who();,return1;,此时,程序执行的结果为,thisistheclassofbase1!,thisistheclassofbase2!,thisistheclassofderive,!,thisistheclassofbase2!,从上面的例子看出,派生类,derive,中的函数,who(),在不同的场合呈现不同的性质。如相对,base1,路径,由于在,base1,中的,who(),函数前有关键字,virtual,,所以它是一个虚函数;若相对于,base2,派生路径,在,base2,中的,who(),函数为一般函数,所以,此时它只是一个重载函数。,当,base1,类指针指向,derive,类对象,obj3,时,函数,who(),就呈现出虚特性;当,base2,类指针指向,derive,类对象,obj3,时,函数只呈现一般的重载特性。,若一个派生类,它的多个基类中有公共的基类,在公共基类中定义一个虚函数,则多重派生以后仍可以重新定义虚函数,也就是说,虚特性是可以传递的。请看下面的例题。,【例8-7】多继承中虚特性的传递例题。,#,include,classbase,/,定义基类,base,public:,virtualvoidwho,()/,定义虚函数,cout,thisistheclassofbase,!,endl,;,;,classbase1:,publicbase,/,定义派生类,base1,public:,voidwho,(),cout,thisistheclassofbase1!,endl,;,;,classbase2:,publicbase,/,定义派生类类,base2,public:,voidwho,(),cout,thisistheclassofbase2!,endl,;,;,classderive,:publicbase1,publicbase2/,定义派生类,derive,public:,voidwho,(),cout,thisistheclassofderive,!who();,ptr2=&,obj,;,ptr2-who();,return1;,此时,程序执行的结果为,thisistheclassofderive,!,thisistheclassofderive,!,从本例题可以看出,虚特性是可以传递的。,base,类作为,base1,和,base2,类的直接基类,它的成员函数,who(),被声明为虚函数,则,base1,和,base2,类中的,who(),都具有虚特性,即均为虚函数;而,derive,类为,base1,和,base2,类的派生类,因此,它的成员函数,who(),也为虚函数。,8.3.3 虚函数的限制,如果我们将所有的成员函数都设置为虚函数,当然是很有益的。它除了会增加一些额外的资源开销,没有什么坏处。但设置虚函数须注意以下几点。,只有成员函数才能声明为虚函数。因为虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。,虚函数必须是非静态成员函数。这是因为静态成员函数不受限于某个对象。,内联函数不能声明为虚函数。因为内联函数不能在运行中动态确定其位置。,构造函数不能声明为虚函数。多态是指不同的对象对同一消息有不同的行为特性。虚函数作为运行过程中多态的基础,主要是针对对象的,而构造函数是在对象产生之前运行的,因此,虚构造函数是没有意义的。,析构函数可以声明为虚函数。析构函数的功能是在该类对象消亡之前进行一些必要的清理工作。析构函数没有类型,也没有参数,和普通成员函数相比,虚析构函数情况略为简单些。,虚析构函数的声明语法如下:,virtual,类名,例如:,classB,public:,/,virtualB();,;,8.4 抽象类,8.4.1 纯虚函数,一个抽象类至少带有一个纯虚函数。纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的实现内容。纯虚函数的声明形式如下:,virtual(参数表)=0,纯虚函数与一般虚函数在书写形式上的不同在于其后面加了“=0”,表明在基类中不用定义该函数,它的实现部分函数体留给派生类去做。,8.4.2 抽象类,抽象类的主要作用是通过它为一个类族建立一个公共的接口,使它们能够更有效地发挥多态特性。使用抽象类时需注意以下几点。,抽象类只能用作其它类的基类,不能建立抽象类对象。抽象类处于继承层次结构的较上层,一个抽象类自身无法实例化,而只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化。,抽象类不能用作参数类型、函数返回值或显式转换的类型。,可以声明一个抽象类的指针和引用。通过指针或引用,我们就可以指向并访问派生类对象,以访问派生类的成员。,抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以声明自己的对象,因而不再是抽象类;反之,如果派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。,【例8-8】抽象类例题。,我们来看这个例题。在基类,Shapes,中将成员,display(),声明为纯虚函数,这样,基类,Shapes,就是一个抽象类,我们无法声明,Shapes,类的对象,但是可以声明,Shapes,类的指针和引用。,Shapes,类经过公有派生产生了,Rectangle,类和,Circle,类。使用抽象类,Shapes,类型的指针,当它指向某个派生类的对象时,就可以通过它访问该对象的虚成员函数。,#,include,constdoublePI,=3.14159;,classShapes,/,抽象基类,Shapes,声明,protected:,intx,y;,public:,voidsetvalue,(,intxx,intyy,=0)x=,xx,;y=,yy,;,virtualvoiddisplay,()=0;/,纯虚函数成员,;,classRectangle,:,publicShapes,/,派生类,Rectangle,声明,public:/,虚成员函数,voiddisplay,(),cout,Theareaofrectangleis,:x*y,endl,;,;,classCircle,:,publicShapes,/,派生类,Circle,声明,public:/,虚成员函数,voiddisplay,(),cout,Theareaofcircleis,:PI*x*x,setvalue,(5,8);,ptr,0-display();,ptr,1=/,指针指向,Circle,类对象,ptr,1-,setvalue,(10);,ptr,1-display();,程序中类,Shapes、Rectangle,和,Circle,属于同一个类族,抽象类,Shapes,通过纯虚函数为整个类族提供了通用的外部接口语义。通过公有派生而来的子类给出了纯虚函数的具体函数体实现,因此是非抽象类。我们可以定义非抽象类的对象,同时根据赋值兼容规则,抽象类,Shapes,类型的指针也可以指向任何一个派生类的对象,通过基类,Shapes,的指针可以访问到正在指向的派生类,Rectangle,和,Circle,类对象的成员,这样就实现了对同一类族中的对象进行统一的多态处理。,本例的程序运行结果为,Theareaofrectangleis,:40,Theareaofcircleis,:314.159,另外,程序中派生类的虚成员函数,display(),并没有用关键字,virtual,显式说明,因为它们与基类的纯虚函数具有相同的名称及参数和返回值,由系统自动判断确定其为虚成员函数。,
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 百科休闲 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服