版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第六章多态性与虚函数第一页,共八十页,2022年,8月28日封装性和继承性为软件设计提供了可靠的安全性和良好的结构层次。在此基础上,多态性使软件中的对象行为更加符合软件所模拟的客观世界中对象行为的多样性,为软件设计中的功能实现和任务调度提供了灵活性。多态性是指当不同的对象收到相同的消息时产生不同的动作。即调用同一个函数名,可以根据需要,实现不同的功能。C++支持两种多态性:编译时多态性和运行时多态性。本章所叙述的内容是运行时多态性的工作机制和实现方法。第二页,共八十页,2022年,8月28日编译时多态性:又称静态多态,是指在编译阶段根据函数名和参数确定应该调用哪一个函数,其过程称为静态关联或早期关联。运行时多态性:又称为动态多态性,是指在编译阶段根据函数名和参数无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况来动态地确定。这种关联称为动态关联或后期关联。实现的途径是:继承和虚函数。只使用编译时多态性机制的程序设计只能称为基于对象(ObjectBased)的程序设计,使用了运行时多态性机制,才能称为是面向对象(ObjectOriented)的程序设计。第三页,共八十页,2022年,8月28日所谓运行多态性是相对与编译多态性而言的。二者的共同作用是实现对象行为的多样性,但在实现方法上和对程序运行控制的作用上有着很大的不同。1编译多态性的局限性由于编译多态性是通过函数重载实现的,因此对象行为的确定(函数的运行绑定)只能由编译器根据程序源代码的安排在编译过程中实现。这样实现的对象行为多样性虽然具有良好的可读性,但在控制程序运行和对象行为多样性方面存在着局限性:6.1运行多态性第四页,共八十页,2022年,8月28日⑴对象行为的多样性必须是预先确定可知的,程序运行的控制必须是预先规划固定的,因此只能解决软件所模拟的客观世界中那些可以预先确定和控制的事件的实现。⑵调用重载函数的不同版本(确定对象行为的多样性)是依据参数(个数和类型)的差别实现的(派生类对基类同名、同参数成员函数的覆盖除外),即发送给不同对象的消息并不完全一致。从这一点分析,编译多态性所实现的并非完备意义上的多态性,即不同对象接收到同一函数调用(函数名和参数完全一致),表现出不同的行为。第五页,共八十页,2022年,8月28日2运行多态性要实现的目标运行多态性要实现的目标是在保持编译多态性所能实现的功能的基础上,克服编译多态性的局限性:⑴能根据程序运行中状态的变化,动态确定接收函数调用的对象和对象行为的多样性,使得软件能够实现所模拟的客观世界中那些无法预先确定事件的发生和完成。⑵完全相同的函数调用(函数名和参数完全一致)能够触发(调用)不同类对象的特定行为,从而实现完备意义的多态性,使得复杂的对象行为控制变得更加简单、统一。第六页,共八十页,2022年,8月28日3运行多态性的实现机制使程序具有运行多态性的关键是能否在程序运行期间,根据程序的运行状态,动态地修改程序的运行控制指针。C++(包括C)的编程要素——指针变量为动态多态性提供了基本实现机制,即程序能够在运行期间通过修改指针变量的当前值,动态地确定程序的当前操作和操作所施加的数据(对象)。指针变量分为两种:第七页,共八十页,2022年,8月28日⑴数据指针变量用于动态确定操作数据,即通过指针而不是使用数据变量名访问数据变量。其定义格式为:数据类型*指针变量名;例如:double*pt;数据指针变量的值是数据空间的首地址,赋值合法性依据是首地址所指示数据的类型。例如:doubled;inti;pt=&d;//合法pt=&i;//非法第八页,共八十页,2022年,8月28日⑵函数指针变量用于动态确定操作,即通过指针而不是使用函数名实现函数调用。其定义格式为:返回数据类型(*指针变量名)(参数据类型,…);例如:int(*fun)(int,int);函数指针变量的值是函数的调用地址,赋值合法性依据是调用地址所指示的函数类型(函数的返回类型,参数个数、类型和顺序)。例如:intmax(int,int);voidcount(double);fun=max;//合法或fun=&max;fun=count;//非法第九页,共八十页,2022年,8月28日在面向对象程序设计中操作和操作所施加的数据是绑定和封装在对象中的。因此,实现动态多态性是在程序运行期间,根据运行状态,动态改变接收同一函数调用的对象,由对象的行为确定操作和操作所施加的数据,从而实现动态地修改程序的运行控制指针。响应同一函数要求被调用的对象成员函数原型(函数名,返回类型,参数个数、类型和顺序)一致,显然这是函数重载所不能支持的。函数指针虽然能够支持动态多态性,但在面向对象程序设计中直接使用函数指针既烦琐又违背面向对象的设计原则。因此必须引入新的编程机制——虚函数。6.2虚函数第十页,共八十页,2022年,8月28日通过指针变量动态确定接收同一函数的对象是实现动态多态性的第一步,首要条件。1一般对象的指针⑴一般对象的指针与系统预定义数据的指针,在定义和使用语法上都相同;⑵无派生关系的不同类对象指针之间不允许相互转换,如果强制转换,可能会引起不可预期的结果。例如:6.2.1对象指针第十一页,共八十页,2022年,8月28日#include<iostream>classclass1{inta,b;public:class1(intx,inty){a=x;b=y;}voidshow(){cout<<"my_base-----------\n";cout<<a<<""<<b<<endl;}};第十二页,共八十页,2022年,8月28日classclass2{intc;public:class2(intx){c=x;}voidshow(){cout<<"my_class----------\n";cout<<"c="<<c<<endl;}};第十三页,共八十页,2022年,8月28日voidmain(){class1c1(50,50),*ptr;class2c2(30);ptr=&c1;ptr->show();ptr=&c2;//错误ptr->show();}如果将代码ptr=&c2;修改为ptr=(class1*)&c2;则会引起不可预测的执行结果,例如:my_base-----------5050my_base-----------306684136第十四页,共八十页,2022年,8月28日2具有派生层次的类对象指针由于派生类是由基类继承定义的,即派生类的对象中总是包含着基类对象部分,并在派生类对象的创建过程中首先被创建。因此,基类对象和派生类对象的首地址所指示的对象类型是向后兼容的,即允许基类指针既可以指向基类对象,也可以指向派生类对象。第十五页,共八十页,2022年,8月28日使用基类指针时要注意以下几个问题:⑴基类指针可以指向该基类的公有派生类对象,但不能自动指向该基类的私有派生类对象(除非使用强制转换实现)。例如:classbase{...};classderive:base{...};voidmain(voidmain(){baseobj1,*ptr;deriveobj2;ptr=&obj1;ptr=&obj2;//不能自动指向其私有派生类对象,但可
//强制转换,例如ptr=(base*)&obj2。}第十六页,共八十页,2022年,8月28日子类型
如果一个特定的类型S,当且仅当它提供了类型T的行为时,则称类型S是类型T的子类型。子类型体现了类型间的一般与特殊的关系。在C++中,子类型的概念是通过公有继承(或公有派生)来实现的。根据继承方式的概念,我们知道,按公有继承的方式产生的派生类中,必然包含了原来基类中的全部成员。因此,一个公有派生类的对象可以提供其基类对象的全部行为(基类的全部接口),也就是说,在程序中可以把一个公有派生类对象当作其基类对象来处理。TS公有继承第十七页,共八十页,2022年,8月28日例:子类型的概念及实现示例。#include<iostream.h>classA//定义类A{private:inta;public:A(inti=0){a=i;}voidprint();};voidA::print(){cout<<"InclassA,print()iscalled."<<endl;}第十八页,共八十页,2022年,8月28日classB:publicA//定义类B,类B是类A的公有派生类{private:intb;public:B(intj=-1){b=j;}};//B是A的子类型voidcommfun(A&aref){aref.print();}第十九页,共八十页,2022年,8月28日voidmain(){Aa;commfun(a);//以基类A的对象a作为实参调用函数commfunBb;commfun(b);//以派生类B的对象b调用函数commfun
//即将公有派生类对象b当作其基类A的对象处理}程序运行结果:InclassA,print()iscalled.InclassA,print()iscalled.第二十页,共八十页,2022年,8月28日说明:(1)在本例中,类B是类A的公有派生类,函数commfun()的形参是一个基类A对象的引用,所以在main函数中,把基类A的对象a作为实参调用函数commfun()时,产生的结果是不言而喻的。但在main函数中,当把类B的对象b作为实参调用函数commfun()时,函数commfun()仍能正常工作,且打印结果与对象a作为实参时的结果相同,这说明,在程序中可以把一个公有派生类对象当作其基类对象来处理。(2)将类型B的对象b传递给函数commfun()处理是在程序运行时发生的。但在程序编译时,编译器只能对源程序代码进行静态检查。P189例5.10第二十一页,共八十页,2022年,8月28日⑵不允许派生类指针指向该派生类的基类对象。例如:classbase{...};classderive:publicbase{...};voidmain(){baseobj1;deriveobj2,*ptr;ptr=&obj2;ptr=&obj1;//派生类指针不能指向它的基类对象}第二十二页,共八十页,2022年,8月28日⑶使用基类指针访问公有派生类对象时,只能自动访问派生类对象中的基类对象部分。例如:classbase{…public:voidshow();};classderive:publicbase{...public:voidshow();};第二十三页,共八十页,2022年,8月28日基类对象派生类对象Baseb;Derived;Base*basep;basepbasep=&b;basepbasep=&d;basep只能引用从基类继承来的成员。xShow()xShow()yShow()basep->Show();basep->Show()基类指针派生类对象基类对象第二十四页,共八十页,2022年,8月28日classPoint{ floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
floatarea(void) { return0.0; }};constfloatPi=3.14159;classCircle:publicPoint{ //类Point的派生类 floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius; }};voidmain(void){Point*pp; //基类指针,可以将派生类对象的地址赋给基类指针Circlec(5.4321);pp=&c;cout<<pp->area()<<endl; //调用的是基类中有的公有函数}在基类和派生类中具有相同的公有函数area()。在这种情况下,使用基类的指针时,只能访问从相应基类中继承来的成员,而不允许访问在派生类中增加的成员。输出为0第二十五页,共八十页,2022年,8月28日通过前面的分析不难看出,面向对象的继承特性已经为实现动态多态性提供了两个基本条件:⑴在具有派生层次结构的各个类中定义同原型(函数名,返回类型和参数个数、类型、顺序均一致)而操作各异的成员函数为不同类对象响应同一函数调用实现行为多样性提供了条件。⑵使用基类指针既可以访问基类对象又可以访问派生类对象为动态确定接收同一函数调用的不同类对象提供了条件。6.2.2为什么要引入虚函数第二十六页,共八十页,2022年,8月28日但仅有上述两个条件仍然无法实现动态多态性,因为要通过基类指针调用派生类的成员函数,必须将基类指针强制转换为派生类指针。实现这样的强制转换必须在编译时进行静态绑定,而不能实现运行时的动态绑定。因此,必须引入一种新的编程机制,使得对派生层次结构中各类的同原型成员函数的调用既能够通过函数名编译绑定,又能够通过函数的调用地址运行绑定。显然,实现运行绑定的最好途径是使用函数指针。这种新的编程机制就是虚函数。第二十七页,共八十页,2022年,8月28日1虚函数的定义⑴虚函数是用来定义基类和公有派生类的同名且同类型成员函数之间的多态关联关系的实现机制;⑵虚函数必须在基类中首先定义,方法是在要定义为虚函数的成员函数声明中冠以关键字virtual;⑶虚函数一旦被定义,可以在一个或多个直接或间接的公有派生类中重新定义;⑷虚函数重新定义时,其函数原型,即包括类型、函数名、参数(类型、个数和顺序),都必须与基类中的原型完全一致。例如:6.2.3虚函数的定义及使用第二十八页,共八十页,2022年,8月28日classbase{…public:voidshow();};classderive:publicbase{...public:voidshow();};第二十九页,共八十页,2022年,8月28日基类对象派生类对象Baseb;Derived;basepbasepxShow()xShow()yShow()basep->Show()Base*basep;basep=&b;basep=&d;basep->Show();即指向派生类新增的成员函数需要将基类中的Show()说明为虚函数第三十页,共八十页,2022年,8月28日classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
virtual
floatarea(void) {return0.0;}};constfloatPi=3.14159;classCircle:publicPoint{ //类Point的派生类 floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius;}};voidmain(void){Point*pp; //基类指针,可以将派生类对象的地址赋给基类指针Circlec(5.4321);pp=&c;cout<<pp->area()<<endl;//调用虚函数}将area()声明为虚函数,编译器对其进行动态聚束,按照实际对象c调用了Circle中的函数area()。使Point类中的area()与Circle类中的area()有一个统一的接口。输出:92.7011声明为虚函数调用虚函数虚函数再定义第三十一页,共八十页,2022年,8月28日#include<iostream.h>classbase{…public:virtualvoidwho(){cout<<"base\n";}//定义虚函数virtualvoidshow(){cout<<“propertiesofbase:…\n";}//定义虚函数};classfirst:publicbase{…public:voidwho(){cout<<"thefirstderivation\n";}//重新定义虚函数};第三十二页,共八十页,2022年,8月28日classsecond:publicfirst{…public:voidwho(){cout<<"thesecondderivation\n";}//重新定义虚函数};第三十三页,共八十页,2022年,8月28日intmain(){baseobj1,*ptr;firstobj2;secondobj3;ptr=&obj1;ptr->who();//调用base类的who()版本ptr->show();//调用base类的show()版本ptr=&obj2;ptr->who();//调用first类的who()版本ptr->show();//调用base类的show()版本第三十四页,共八十页,2022年,8月28日ptr=&obj3;ptr->who();//调用second类的who()版本ptr->show();//调用base类的show()版本return0;}执行结果:basepropertiesofbase:…thefirstderivationpropertiesofbase:…thesecondderivationpropertiesofbase:…第三十五页,共八十页,2022年,8月28日得到上述理想的动态多态性结果的原因是:⑴定义了虚函数的基类增加了一个存放虚函数调用地址函数指针表virtualtable,该表的大小取决于虚函数的个数,该表的首地址由基类的内部指针vptr指示。⑵在派生层次中的各个派生类会从基类中直接或间接继承虚函数指针表virtualtable和指针vptr。如果虚函数在派生类被重新定义,则virtualtable中的调用地址将被重新定义虚函数的调用地址替换,否则仍然使用直接基类虚函数的调用地址。第三十六页,共八十页,2022年,8月28日⑶调用虚函数的运行绑定是由基类指针动态指向派生层次中某个类的对象,通过该对象的内部指针vptr所指示的virtualtable中的相应调用地址实现的。下图示意了虚函数的定义和调用绑定关系:第三十七页,共八十页,2022年,8月28日第三十八页,共八十页,2022年,8月28日2虚函数与重载函数的区别⑴虚函数的首次定义和重新定义实际上是在类派生定义层次中的同名函数覆盖定义。因此,虚函数的首次定义必须在基类中,重新定义只能在基类的不同派生类中,而不能发生在同一类定义中,也不能是全局函数。而函数重载既可以发生在同一类定义中,也可以是全局函数。第三十九页,共八十页,2022年,8月28日⑵函数重载必须具有相同的函数名和不同的参数(个数、类型或顺序)。而函数的返回类型不是区别重载函数不同版本的标志,但如果两个函数只是返回类型不同,则会导致二义性错误。而虚函数重新定义必须保持与基类中的虚函数原型(返回类型、函数名、参数类型以及个数和顺序)完全一致,否则会产生两种情况:①如果仅返回类型不同,则会导致二义性错误。②如果仅函数名相同,而函数的参数有所不同,则系统会将该函数作为一般的重载函数处理,从而失去虚函数特性。第四十页,共八十页,2022年,8月28日归纳虚函数的定义和使用应注意下几点:⑴在基类中,使用virtual可以将其public或protected部分的成员函数声明为虚函数。⑵派生类对基类中声明的虚函数进行重新定义时,关键字virtual可以忽略。但为了增强程序的可读性,有时在派生类定义中,虚函数重定义时也使用关键字virtual。⑶虚函数被重新定义时,其函数原型与基类中的虚函数原型必须完全相同。第四十一页,共八十页,2022年,8月28日⑷在一个派生类层次结构中定义了虚函数后,就允许在执行过程中,动态改变基类指针所指向的该基类派生的不同类对象,从而调用由虚函数确定的统一接口的不同操作版本。显然,虚函数充分体现了面向对象程序设计的动态多态性。⑸使用对象名和点运算符的方式也可以调用虚函数的不同版本,但是这种调用是在编译时实现的静态联编,没有充分利用虚函数的特性。因此,不需要通过基类指针实现运行时多态性调用的成员函数不应该定义为虚函数,避免不必要的内存开销。第四十二页,共八十页,2022年,8月28日classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
virtual
floatarea(void) {return0.0;}//声明为虚函数};constfloatPi=3.14159;classCircle:publicPoint{ //类Point的派生类 floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius;}//虚函数再定义};voidmain(void){Point*pp; //基类指针,可以将派生类对象的地址赋给基类指针Circlec(5.4321);cout<<c.area()<<endl;cout<<c.Point::area()<<endl; cout<<c.Circle::area()<<endl;}输出:92.7011 0 92.7011可见,利用对象名进行调用与一般非虚函数没有区别。用对象名调用area()第四十三页,共八十页,2022年,8月28日⑹在派生类中,直接基类的虚函数指针表virtualtable总是被自动继承。所以一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。因此,虚函数特别适合定义一族类的统一接口。⑺虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数,因为虚函数调用要靠特定的对象来决定应该动态绑定虚函数的哪个版本。但是虚函数可以被声明为另一个类的友元成员。第四十四页,共八十页,2022年,8月28日⑻构造函数不允许是虚函数,但析构函数可以是虚函数。如果一个基类的析构函数被说明为虚函数,则它的派生类的析构函数也是虚函数(不管它是否用关键字virtual进行了说明)。虚析构函数的作用是在使用delete运算符删除一个对象时,能确保析构函数被正确地调用,即使用基类指针,以动态绑定应该被调用析构函数,正确地释放当前基类指针所指向的类对象所占用的空间。例如:第四十五页,共八十页,2022年,8月28日#include<iostream.h>classA{public:virtual~A(){cout<<"调用A::~A()"<<endl;}};classB:publicA{char*buf;public:B(inti){buf=newchar[i];}virtual~B(){delete[]buf;cout<<"调用B::~B()"<<endl;}};第四十六页,共八十页,2022年,8月28日voidfun(A*a){deletea;}voidmain(){A*a=newB(10);fun(a);}运行结果:调用B::~B()调用A::~A()如果A的析构函数不是虚函数,则输出结果如下:调用A::~A()显然,调用A::~A()释放B类对象是不正确的。第四十七页,共八十页,2022年,8月28日例:用于求解三角形、正方形和圆面积的简单程序。1问题分析:⑴三角形面积:底*高/2⑵正方形面积:边长*边长⑶圆面积:半径*半径*π归纳这些算法可以抽象出统一的表达式:x*y*C其中C为常量。对于不同的几何图形,其值不同:三角型:C=½正方形:C=1圆形:C=π不难看出,将面积求解设计为不同几何图形的统一接口有利于程序的结构化和灵活性要求。6.2.4虚函数应用实例第四十八页,共八十页,2022年,8月28日为此,可以设计一个以概念图形类figure为基类,三角形类triangle、正方形类square和圆类circles为figure的公有派生类的几何图形类族的层次结构。该结构中的各类具有统一的面积求解操作接口,即在figure中定义一个计算面积的虚函数show_area,其原型(统一接口描述)如下:virtualdoubleshow_area();并在各个派生类中重新定义该虚函数show_area的不同操作版本,使程序可以通过一个基类指针动态选择不同的几何图形对象求解面积的目的。第四十九页,共八十页,2022年,8月28日第五十页,共八十页,2022年,8月28日例:能在一个链表中保存学生、教师和职员三种不同信息的程序。1.问题分析:⑴链表结构要求组成链表的结点必须具有相同类型的值域,用于保存数据。⑵需要被保存的信息类型是不同的;如何在一个链表中保存不同类型的数据,即设计一个“异质链表”就成为实现问题需求的关键。改变链表结构的基本性质是不可能的,因此解决问题的方法是如何将不同类型数据转换为同一类型族数据,第五十一页,共八十页,2022年,8月28日⑴归纳学生、教师和职员信息的共性,可以设计一个具有共同信息数据属性的人员结点类person,并以person为基类公有派生学生结点类student、教师结点类teacher和职员结点类staff组成一个人员信息结点类族,并在基类person中定义将类对象插入链表和显示信息的统一操作接口:virtualvoidinsert();virtualvoidprint();并在student、teacher和staff的定义中重新定义insert和print的不同操作版本第五十二页,共八十页,2022年,8月28日⑵设计一个链表类list。其链域指针类型为person类型。使得链表的插入操作可以根据被插入的对象类型不同,动态绑定insert的不同操作版本,实现将不同类对象插入同一链表的目的。下面的类图描述了各类的属性、行为和类之间的相互关系:第五十三页,共八十页,2022年,8月28日第五十四页,共八十页,2022年,8月28日第五十五页,共八十页,2022年,8月28日第五十六页,共八十页,2022年,8月28日2详细设计:⑴类设计:①基类personⅰ类定义:classperson{friendclasslist;//声明链表类list为友元类protected:charname[20];//人员姓名intage;//人员年龄charadd[40];//人员住址chartele[15];//人员电话staticperson*ptr;//指向被插入对象的指针person*next;//链域指针第五十七页,共八十页,2022年,8月28日public:person(char*name,intage,char*add,char*tele);virtualvoidprint();//显示人员的共同信息virtualvoidinsert(){}//插入操作};ⅱ算法描述:print():使用标准输出流cout顺序按行输出各类人员的共同信息(姓名name、年龄age、住址add和电话tele)数据和相应的提示信息。insert():在本类不需要定义任何插入操作。第五十八页,共八十页,2022年,8月28日②派生类studentⅰ类定义:classstudentpublicperson{friendclasslist;intlevel;floatgrade_point_averagefloataverage;public:student(char*name,intage,char*add,char*teleintlevel,floatgrade_point_average);voidprint();voidinsert();};第五十九页,共八十页,2022年,8月28日ⅱ算法描述:
print():在调用person::print显示人员共同信息的基础上,增加学生类student的新增信息(年级level和平均成绩grade_point_average)和信息提示的显示行。
insert():在堆中动态复制student类对象,并使插入对象指针ptr指向堆中的student类对象。第六十页,共八十页,2022年,8月28日③派生类teacherⅰ类定义:classteacherpublicperson{friendclasslist;floatsalary;public:teacher(char*name,intage,char*add,char*tele,floatsalary);voidprint();voidinsert();}第六十一页,共八十页,2022年,8月28日ⅱ算法描述:
print():在调用person::print显示人员共同信息的基础上,增加教师类teacher的新增信息(薪金salary)和信息提示的显示行。insert():在堆中动态复制teacher类对象,并使插入对象指针ptr指向堆中的tacher类对象。第六十二页,共八十页,2022年,8月28日④派生类staffⅰ类定义:classstaff:publicperson{friendclasslist;floathourly_wages;public:teacher(char*name,intage,char*add,char*telefloathourly_wages);voidprint();voidinsert();};第六十三页,共八十页,2022年,8月28日ⅱ算法描述:
print():在调用person::print显示人员共同信息的基础上,增加职员类staff的新增信息(计时工资hourly_wages)和信息提示的显示行。insert():在堆中动态复制staff类对象,并使插入对象指针ptr指向堆中的staff类对象。第六十四页,共八十页,2022年,8月28日⑤链表类listⅰ类定义:classlist{person*root;publicpublic:list();voidinsert_person(person*node);voidremove(char*name);voidprint_list();};第六十五页,共八十页,2022年,8月28日第六十六页,共八十页,2022年,8月28日第六十七页,共八十页,2022年,8月28日第六十八页,共八十页,2022年,8月28日第六十九页,共八十页,2022年,8月28日6.3.1纯虚函数和抽象类1纯虚函数的概念从前面的实例中不难看出,有时定义基类只是描述一种概念,并不与任何具体的有意义的实例相联系。因此这些基类的某些接口函数只须为它的公有派生类提供统一的接口规则,而无须也无法提供具体操作功能。例如,上例中person类的插入操作虚函数Insert。6.3抽象类第七十页,共八十页,2022年,8月28日所以这类虚函数常常被定义为纯虚函数。纯虚函数的定义形式:virtualtype函数名(参数表)=0;例如:第七十一页,共八十页,2022年,8月28日classshape{…public:virtualvoidshow()=0;//定义纯虚函数virtualfloatarea()=0;//定义纯虚函数…};classrectangles:publicshape{…public:voidshow();
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年男士箱包行业分析报告及未来发展趋势报告
- 2026年半导体石英管行业分析报告及未来发展趋势报告
- 2026年智慧商圈建设行业分析报告及未来发展趋势报告
- 2026年环保检测行业分析报告及未来发展趋势报告
- 2026年衣物除尘滚行业分析报告及未来发展趋势报告
- 2026年煤矿循环经济园区行业分析报告及未来发展趋势报告
- 2026年压花辊行业分析报告及未来发展趋势报告
- 2026年吸湿杯垫行业分析报告及未来发展趋势报告
- 2026年进排气系统行业分析报告及未来发展趋势报告
- 2026年造船行业分析报告及未来发展趋势报告
- 指导老年人使用拐杖行走
- DB61∕T 1343-2020 地理标志产品柞水木耳
- 二级配电箱安全操作规程
- 2025年一级注册计量师真题答案解析
- 《防止电力建设工程施工安全事故三十项重点要求》宣贯与解读
- 新生儿乳糖不耐受诊断和治疗专家共识(2025年)解读 4
- 高校非学历教育质量评估标准
- 中信集团商标管理办法
- 格力多联机空调维护保养手册
- 国家职业技能标准 4-08-08-07 室内装饰设计师(2023年版)
- 2025年湖南省长沙市初中学业水平考试中考(会考)生物试卷(真题+答案)
评论
0/150
提交评论