




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1第第14章章 虚函数虚函数2虚函数虚函数多态性:多态性:调用同一个函数名,可以根据需要调用同一个函数名,可以根据需要但实现不同的功能。但实现不同的功能。多态性是面向对象的程序设计的关键技术。多态性是面向对象的程序设计的关键技术。编译时的多态性(函数重载)编译时的多态性(函数重载)运行时的多态性运行时的多态性(虚函数)(虚函数)多态性多态性运行时的多态性是指在程序执行之前,根据函数名和参数运行时的多态性是指在程序执行之前,根据函数名和参数无法确定应该调用哪一个函数,必须在程序的执行过程中,无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况来动态地确定根据具体的执行情况来动态
2、地确定3可以将一个派生类对象的地址赋给基类的指针变可以将一个派生类对象的地址赋给基类的指针变量量。基类对象基类对象派生类对象派生类对象Base b;Derive d;Base *basep;basepbasep=&b;basepbasep = &d;basep只能引用从基只能引用从基类继承来的成员。类继承来的成员。xShow()xShow()yShow()basep -Show();basep-Show()基类指针基类指针派生类对象派生类对象基类对象基类对象4class Pointfloat x,y;public: Point()Point(float i,float j)x=
3、i;y=j;float area(void) return 0.0;const float Pi=3.14159;class Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r)radius=r;float area(void) return Pi*radius*radius;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); pp=&c; coutarea ()Sho
4、w()Base *basep;basep=&b;basep = &d;basep -Show();即指向派生类新增的成员函数即指向派生类新增的成员函数需要将基类中的需要将基类中的Show()说明为虚函数说明为虚函数6若要访问派生类中相同名字的函数,必须将若要访问派生类中相同名字的函数,必须将基类中的基类中的同名函数定义为虚函数同名函数定义为虚函数,这样,将,这样,将不同的派生类对象的地址赋给基类的指针变不同的派生类对象的地址赋给基类的指针变量后,就可以量后,就可以动态地根据这种赋值语句调用动态地根据这种赋值语句调用不同类中的函数不同类中的函数。7class Point floa
5、t x,y;public: Point()Point(float i,float j)x=i;y=j;virtual float area(void) return 0.0; ;const float Pi=3.14159;class Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r)radius=r;float area(void) return Pi*radius*radius;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类
6、对象的地址赋给基类指针 Circle c(5.4321); pp=&c; coutarea ()endl; /调用虚函数调用虚函数将将area()声明为虚函数,编译器对其进行动态聚束,按照实际对象声明为虚函数,编译器对其进行动态聚束,按照实际对象c调用了调用了Circle中的函数中的函数area()。使。使Point类中的类中的area()与与Circle类中的类中的area()有一个统一有一个统一的接口。的接口。输出:输出:92.7011声明为虚函数声明为虚函数调用虚函数调用虚函数虚函数再定义虚函数再定义8虚函数的定义和使用虚函数的定义和使用 可以在程序运行时通过调用相同的函数名而实
7、可以在程序运行时通过调用相同的函数名而实现不同功能的函数称为虚函数。现不同功能的函数称为虚函数。定义格式为:定义格式为:virtual FuncName();一旦把基类的成员函数定义为虚函数,由基类所一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数派生出来的所有派生类中,该函数均保持虚函数的特性。的特性。 在派生类中重新定义基类中的虚函数时,可以不在派生类中重新定义基类中的虚函数时,可以不用关键字用关键字virtual来修饰这个成员函数来修饰这个成员函数 。9虚函数是用关键字虚函数是用关键字virtual修饰的某基类中的修饰的某基类中的protected或或
8、public成员函数。它可以在派生成员函数。它可以在派生类中重新定义,以形成不同版本。类中重新定义,以形成不同版本。只有在程只有在程序的执行过程中,依据指针具体指向哪个类序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束活哪一个版本,实现动态聚束。10class Aprotected:int x;public: A()x =1000; virtual void print()cout “x=”xt;/虚函数虚函数;class B:public Aint y;public: B() y=2000;void
9、print()cout “y=”yt;/派生虚函数派生虚函数;class C:public Aint z;public: C()z=3000;void print()cout “z=”zprint();/调用类调用类A的虚函数的虚函数 pa=&b; pa-print();/调用类调用类B的虚函数的虚函数 pa=&c; pa-print();/调用类调用类C的虚函数的虚函数x=1000y=2000z=3000 x=1000y=2000z=300011class Base public : virtual int Set(int a, int b) . .;class Derive
10、:public Basepublic : int Set(int x, int y) . .;class Base public : virtual int Set(int a, int b) . .;class Derive:public Basepublic : int Set(int x, int y=0) . .;int Set(int ,int )是虚函数是虚函数两个两个Set()函数参数函数参数不一致,是重载,不一致,是重载,不是虚函数不是虚函数12关于虚函数,说明以下几点:关于虚函数,说明以下几点:1、当在基类中把成员函数定义为虚函数后,当在基类中把成员函数定义为虚函数后,在其派
11、生类中定义的虚函数必须与基类中的在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同数必须一一对应,函数的返回的类型也相同。若函数名相同,但参数的个数不同或者参数若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成虚函数。若函数名不同,显然这是不同的成员函数。员函数。132、实现这种动态的多态性时,必须使用实现这种动态的多态性时,必须使用基类类型基类类型的指针变量或基类引用的指针变量或基类引用,并
12、使该指针或引用,并使该指针或引用指向指向不同的派生类对象不同的派生类对象,并通过调用指针或引用所指,并通过调用指针或引用所指向的虚函数才能实现动态的多态性。向的虚函数才能实现动态的多态性。xShow()xShow()yShow()xShow()zShow()类类A类类B类类CShow()定义为虚函数定义为虚函数类类B与类与类C均为类均为类A的公有派生。的公有派生。A *p; B b;C c; p=&b ; p-Show();p=&c ; p-Show();即在程序运行时,即在程序运行时,通过赋值语句实通过赋值语句实现多态性现多态性143、虚函数必须是类的一个成员函数,不能是虚函
13、数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。友元函数,也不能是静态的成员函数。4、在派生类中、在派生类中没有重新定义虚函数没有重新定义虚函数时,与一时,与一般的成员函数一样,当调用这种派生类对象般的成员函数一样,当调用这种派生类对象的虚函数时,的虚函数时,则调用其基类中的虚函数则调用其基类中的虚函数。5、可把析构函数定义为虚函数,但是,不能可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数将构造函数定义为虚函数。156、虚函数与一般的成员函数相比较,、虚函数与一般的成员函数相比较,调用时的执调用时的执行速度要慢一些行速度要慢一些。为了实现多态性,在每一个派。为了实
14、现多态性,在每一个派生类中均要保存相应虚函数的入口地址表,函数生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现的。因此,除了要编写的调用机制也是间接实现的。因此,除了要编写一些通用的程序,并一定要使用虚函数才能完成一些通用的程序,并一定要使用虚函数才能完成其功能要求外,通常不必使用虚函数。其功能要求外,通常不必使用虚函数。7、一个函数如果被定义成虚函数,则不管经历多、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性,以实现少次派生,仍将保持其虚特性,以实现“一个接一个接口,多个形态口,多个形态”。16虚函数的访问虚函数的访问用基类指针或基类引用访问与用派生类对
15、象用基类指针或基类引用访问与用派生类对象名访问名访问用基类指针或基类引用访问虚函数时,指向用基类指针或基类引用访问虚函数时,指向其实际派生类对象重新定义的函数。实现动其实际派生类对象重新定义的函数。实现动态聚束。态聚束。通过一个通过一个派生类对象名派生类对象名访问时,只能静态聚访问时,只能静态聚束。即由编译器在编译的时候决定调用哪个束。即由编译器在编译的时候决定调用哪个函数。函数。17class Point float x,y;public: Point()Point(float i,float j)x=i;y=j;virtual float area(void) return 0.0; /声
16、明为虚函数声明为虚函数;const float Pi=3.14159;class Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r)radius=r;float area(void) return Pi*radius*radius;/虚函数再定义虚函数再定义;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); coutc.area()endl; coutc.Point:are
17、a()endl; coutc.Circle:area ()endl;输出:输出:92.7011092.7011可见,利用对象名进行调用与一般非虚函数没有区别。可见,利用对象名进行调用与一般非虚函数没有区别。用对象名调用用对象名调用area( )18class base0public: void v(void)coutbase0n;class base1:public base0public: virtual void v(void) coutbase1n; ;class A1:public base1public: void v()coutA1n; ;class A2:public A1pub
18、lic: void v(void)coutA2n; ;class B1:private base1public: void v(void)coutB1n; ;class B2:public B1public: void v(void)coutv(); A2 a2; (pb=&a2)-v(); B1 b1; (pb=&b1)-v(); B2 b2; (pb=&b2)-v();base0base0私有派生,在类外私有派生,在类外不能调用基类函数不能调用基类函数19class base0public: void v(void)coutbase0n;class base1:pu
19、blic base0public: virtual void v(void) coutbase1n; ;class A1:public base1public: void v()coutA1n; ;class A2:public A1public: void v(void)coutA2n; ;class B1:private base1public: void v(void)coutB1n; ;class B2:public B1public: void v(void)coutv(); A2 a2; (pb=&a2)-v();A1A220下面程序的输出是下面程序的输出是 。class
20、Aprotected:int x;public:A()x =1000;virtual void p()cout x=xn; p2(); virtual void p2()coutA:p2()endl; ;class C:public Aint z;public:C()z=3000; void p()cout z=zn; p2();virtual void p2()coutC:p2()p();pa=&c;pa-p();x=1000z=3000C:p2()A:p2()21class B0class B0public:public: virtual void display()cout B0
21、:display()n; virtual void display()cout B0:display()n; ;class B1:public B0class B1:public B0public:public: void display()cout B1:display()n; void display()cout B1:display()n; ;class D:public B1class D:public B1public:public: void display()cout D:display()n; void display()cout display(); p-display();
22、 void main()void main() B0 b0, B0 b0, * *p; p; p = &b0; fun(p); p = &b0; fun(p); B1 b1; p = &b1; B1 b1; p = &b1; fun(p); fun(p); D d; p = &d; D d; p = &d; fun(p); fun(p); B0:display()B0:display()B1:display()B1:display()D:display()D:display()22class B0class B0public:public: vi
23、rtual void display()cout B0:display()n; virtual void display()cout B0:display()n; ;class B1:public B0class B1:public B0public:public: void display()cout B1:display()n; void display()cout B1:display()n; ;class D:public B1class D:public B1public:public: void display()cout D:display()n; void display()c
24、out D:display()n; ;void fun(void fun(B0 &bbB0 &bb) ) bb.display(); bb.display(); void main()void main() B0 b0; B0 b0; fun(b0); fun(b0); B1 b1; B1 b1; fun(b1); fun(b1); D d; D d; fun(d); fun(d); B0:display()B0:display()B1:display()B1:display()D:display()D:display()23纯虚函数纯虚函数在基类中不对虚函数给出有意义的实现在
25、基类中不对虚函数给出有意义的实现,它只是它只是在派生类中有具体的意义。这时基类中的虚函数在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的目的地由不同的派生类中只是一个入口,具体的目的地由不同的派生类中的对象决定。这个虚函数称为的对象决定。这个虚函数称为纯虚函数纯虚函数。class virtual ()=0;.;24class Aprotected:int x;public: A()x =1000; virtual void print()=0; /定义纯虚函数定义纯虚函数;class B:public A /派生类派生类private: int y;public: B() y=2
26、000;void print()cout “y=”yn;/重新定义纯虚函数重新定义纯虚函数;class C:public A /派生类派生类int z;public: C()z=3000;void print()cout “z=”zprint(); pa=&c; pa-print(); A a; pa=&a; pa-print( );y=2000z=3000抽象类抽象类不能定义抽象类的对象不能定义抽象类的对象251、在定义纯虚函数时,不能定义虚函数的实、在定义纯虚函数时,不能定义虚函数的实现部分。现部分。2、把函数名赋于、把函数名赋于0,本质上是将指向函数体,本质上是将指向函数
27、体的指针值赋为初值的指针值赋为初值0。与定义空函数不一样,。与定义空函数不一样,空函数的函数体为空,即调用该函数时,不空函数的函数体为空,即调用该函数时,不执行任何动作。执行任何动作。在没有重新定义这种纯虚函在没有重新定义这种纯虚函数之前,是不能调用这种函数的。数之前,是不能调用这种函数的。263、把至少包含一个纯虚函数的类,称为抽象把至少包含一个纯虚函数的类,称为抽象类。这种类只能作为派生类的基类,不能用类。这种类只能作为派生类的基类,不能用来说明这种类的对象来说明这种类的对象。其理由是明显的:因为虚函数没有实现部分,其理由是明显的:因为虚函数没有实现部分,所以不能产生对象。但可以定义指向抽
28、象类所以不能产生对象。但可以定义指向抽象类的指针,即指向这种基类的指针。当用这种的指针,即指向这种基类的指针。当用这种基类指针指向其派生类的对象时,基类指针指向其派生类的对象时,必须在派必须在派生类中生类中重载重载纯虚函数,否则会产生程序的运纯虚函数,否则会产生程序的运行错误。行错误。274、在以抽象类作为基类的派生类中必须有在以抽象类作为基类的派生类中必须有纯虚函数的实现部分,即必须有重载纯虚函纯虚函数的实现部分,即必须有重载纯虚函数的函数体。否则,这样的派生类也是不能数的函数体。否则,这样的派生类也是不能产生对象的。产生对象的。综上所述,可把纯虚函数归结为:综上所述,可把纯虚函数归结为:抽
29、象类的抽象类的唯一用途是为派生类提供基类,纯虚函数的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并作用是作为派生类中的成员函数的基础,并实现动态多态性。实现动态多态性。28虚基类虚基类多基派生中的多条路径具有公共基类时,在这条路径的汇合处就会多基派生中的多条路径具有公共基类时,在这条路径的汇合处就会因对公共基类产生多个拷贝而产生同名函数调用的二义性。因对公共基类产生多个拷贝而产生同名函数调用的二义性。解决这个问题的办法就是把解决这个问题的办法就是把公共基类定义为虚基类公共基类定义为虚基类,使由它派生的,使由它派生的多条路径的汇聚处只产生一个拷贝。多条路径的汇聚处只
30、产生一个拷贝。class Base ;class A : public Base ;class B: public Base ;class C: public A, public B ;类类C中继承了两个类中继承了两个类Base,即有两个类即有两个类Base的实现部分,的实现部分,在调用时产生了二义性。在调用时产生了二义性。29用虚基类进行多重派生时,用虚基类进行多重派生时,若虚基类没有缺若虚基类没有缺省的构造函数省的构造函数,则在每一个派生类的构造函,则在每一个派生类的构造函数中数中都必须有对虚基类构造函数的调用都必须有对虚基类构造函数的调用 (且(且首先调用)。首先调用)。由虚基类派生出的
31、对象初始化时,由虚基类派生出的对象初始化时,直接调用直接调用虚基类的构造函数。因此,若将一个类定义虚基类的构造函数。因此,若将一个类定义为虚基类,则一定有正确的构造函数可供所为虚基类,则一定有正确的构造函数可供所有派生类调用。有派生类调用。30class basepublic:virtual void a() couta() in basen;virtual void b() coutb() in basen;virtual void c() coutc() in basen;virtual void d() coutd() in basen;virtual void e() coute()
32、in basen;virtual void f() coutf() in basen;class A:public basepublic:virtual void a() couta() in An;virtual void b() coutb() in An;virtual void f() coutf() in An;class B:public basepublic:virtual void a() couta() in Bn;virtual void b() coutb() in Bn;virtual void c() coutc() in Bn;class C:public A,pu
33、blic Bpublic:virtual void a() couta() in Cn;virtual void d() couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();将类将类C的地址赋的地址赋值时产生歧义值时产生歧义31a( )b( )c( )d( )e( )f( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c( )d( )e( )f( )a( )c( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c( )d( )e( )f( )a( )c( )base
34、ABCa( )d( )AB32class basepublic:virtual void a() couta() in basen;virtual void b() coutb() in basen;virtual void c() coutc() in basen;virtual void d() coutd() in basen;virtual void e() coute() in basen;virtual void f() coutf() in basen;class A:public basepublic:virtual void a() couta() in An;virtual
35、 void b() coutb() in An;virtual void f() coutf() in An;class B:public basepublic:virtual void a() couta() in Bn;virtual void b() coutb() in Bn;virtual void c() coutc() in Bn;class C:public A,public Bpublic:virtual void a() couta() in Cn;virtual void d() couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f(
36、);将类将类C的地址赋的地址赋值时产生歧义值时产生歧义类类C中有两个中有两个base,只有一个,只有一个Aa() in Cb() in Ac() in based() in Ce() in basef() in A为避免这种情况,将为避免这种情况,将base定义定义为虚基类。为虚基类。33class basepublic:virtual void a() couta() in basen;virtual void b() coutb() in basen;virtual void c() coutc() in basen;virtual void d() coutd() in basen;vi
37、rtual void e() coute() in basen;virtual void f() coutf() in basen;class A:virtual public basepublic:virtual void a() couta() in An;virtual void b() coutb() in An;virtual void f() coutf() in An;class B:virtual public basepublic:virtual void a() couta() in Bn;virtual void c() coutc() in Bn;class C:pub
38、lic A,public Bpublic:virtual void a() couta() in Cn;virtual void d() couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();34a( )b( )c( )d( )e( )f( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c( )d( )e( )f( )a( )c( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )c( )baseABCa( )d( )AB35class basepublic:virtual void a() couta() in basen;virtual void b() coutb() in basen;virtual void c() coutc() in basen;virtual void d() coutd() in basen;virtual void e() coute() in basen;virtual void f() coutf() in basen;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年浙江嘉兴市南湖投资开发建设集团有限公司下属公司招聘14人笔试参考题库附带答案详解
- 2024-2025新工人入场安全培训考试试题答案历年考题
- 2025工厂员工安全培训考试试题及参考答案【黄金题型】
- 2025年公司厂级安全培训考试试题及答案(历年真题)
- 2025商业大厦物业管理服务合同
- 2025年双头应急灯合作协议书
- 2025房屋买卖合同
- 2025海商法中船舶航次保险合同研究
- 2025年福建省城市房屋拆迁补偿安置合同协议
- 《员工健康与安全培训》课件
- GB/T 9775-2008纸面石膏板
- GB/T 12604.4-2005无损检测术语声发射检测
- GB/T 1229-2006钢结构用高强度大六角螺母
- 多中心结直肠癌临床研究生物样本库信息系统的建设与管理
- 消防管道支架制作安装标准2017.喷淋
- 曲柄连杆机构拆装教学教材课件
- 合格供应商年度评审计划
- 培训考试汇总金属胶接工艺简介
- Q-RJ 557-2017 航天型号产品禁(限)用工艺目录(公开)
- ZGM95G-1型中速辊式磨煤机使用和维护说明书
- SZ系列GPS标准时间同步钟使用说明
评论
0/150
提交评论