virtual函数与多态_第1页
virtual函数与多态_第2页
virtual函数与多态_第3页
virtual函数与多态_第4页
virtual函数与多态_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C+程序设计实例教程程序设计实例教程第第8章章 virtual函数与多态函数与多态 多态性是面向对象编程三大特性之一,多态性是指对不同类的对象的相似问题采用“统一接口,不同做法”的处理方式。 本章介绍继承结构中基于virtaul函数的动态多态。其中包括virtaul函数、virtaul函数覆盖、纯virtaul函数以及virtaul析构函数的使用方法。 本章的最后对于多继承结构中的virtual继承做了简单的介绍。C+程序设计实例教程程序设计实例教程知识体系知识体系 本章要点:本章要点:8.1多态概述多态概述 8.2virtual函数与函数与virtual函数表函数表 8.3virtual函数

2、的覆盖与多态函数的覆盖与多态 8.4区分隐藏与区分隐藏与virtual函数覆盖函数覆盖 8.5不能动态绑定函数参数的默认值不能动态绑定函数参数的默认值 8.6程序实例程序实例雇员管理雇员管理 8.7纯纯virtual函数与抽象类函数与抽象类 8.8virtual析构函数析构函数 C+程序设计实例教程程序设计实例教程知识体系知识体系:8.9程序实例程序实例画图程序画图程序 8.10RTTI 8.11多继承结构中使用多继承结构中使用virtual析构函数析构函数 8.12virtual继承和继承和virtual基类基类 C+程序设计实例教程程序设计实例教程8.1 多态概述多态概述 多态一词是指不同

3、的对象收到相同的消息,产生不同的动作。 把函数调用和具体的函数执行代码连接在一起的过程称为联编。多态从联编的实现角度分为:静态多态和动态多态。本章介绍的动态多态就是使用指向派生类对象的基类指针(或基类引用),在调用基类和派生类同时具有的同名函数时,能够调用派生类版本的函数。C+语言为实现这种动态多态,为用户提供virtual函数机制,编译器的内部采用动态联编来完成运行时的函数调用与函数体的绑定。 C+程序设计实例教程程序设计实例教程8.2 virtual函数与函数与virtual函数表函数表 当自定义类型中存在virtual函数或其基类中存在virtaul函数时,系统将为该类维护一张virtu

4、al函数表,这里可以使用的virtaul函数包括该类中声明的virtaul函数,也包括该类的基类中声明的非private属性的virtual函数。例如: class A public: virtual void f1(); virtual void f2(int);class B:public A;class C:public Bpublic: virtual int f3();C+程序设计实例教程程序设计实例教程 上述自定义了类型A、B和C都有各自virtual函数表,见表8.18.3。当存在virtual函数表时,该类型的对象需要多占用4个字节的空间 以便为未来动态联编做准备。程序8.1验

5、证了这一点。C+程序设计实例教程程序设计实例教程1 /8.1 指向指向virtual函数表的指针函数表的指针5 class A 6 public: 8 int x; 9 virtual void f1()10 11 virtual void f2(int)12 13 ;14 class B:public A15 int y;17 ;18 class C:public B19 int z;21 public:22 virtual int f3()23 return 0;25 26 ;28 int main()29 30 A a;C+程序设计实例教程程序设计实例教程31 B b; C c;33 c

6、outsizeof(a)=sizeof(a)endl;34 coutsizeof(b)=sizeof(b)endl;35 coutsizeof(c)=sizeof(c)endl;36 cout-endl;38 cout&a=&aendl;39 cout&a.x=&a.xendl;40 cout&b=&bendl;41 cout&b.x=&b.xendl;42 cout&c=&cendl;43 cout&c.x=&c.xendl;44 cout-endl;46 A a1; B b1; C c1;49 coutA类型类型virtual函数表的地址:函数表的地址:*(void *)&a)endl;5

7、0 coutA类型类型virtual函数表的地址:函数表的地址:*(void *)&a1)endl;51 coutB类型类型virtual函数表的地址:函数表的地址:*(void *)&b)endl;52 coutB类型类型virtual函数表的地址:函数表的地址:*(void *)&b1)endl;53 coutC类型类型virtual函数表的地址:函数表的地址:*(void *)&c)endl;54 coutC类型类型virtual函数表的地址:函数表的地址:*(void *)&c1)endl;55 cout-endl;57 return 0;58 显示结果:显示结果:sizeof(a)=

8、8sizeof(b)=12sizeof(c)=16-&a=0012FF78&a.x=0012FF7C&b=0012FF6C&b.x=0012FF70&c=0012FF5C&c.x=0012FF60-A类型类型virtual函数表的地址:函数表的地址:0046C0D8A类型类型virtual函数表的地址:函数表的地址:0046C0D8B类型类型virtual函数表的地址:函数表的地址:0046C0E4B类型类型virtual函数表的地址:函数表的地址:0046C0E4C类型类型virtual函数表的地址:函数表的地址:0046C0F0C类型类型virtual函数表的地址:函数表的地址:0046C

9、0F0-C+程序设计实例教程程序设计实例教程8.3 virtual函数的覆盖与多态函数的覆盖与多态 要想在继承结构中做到指向派生类的基类指针或引用能够调用派生类函数,仅仅在基类中将成员函数声明为virtual函数是不够的,还需要在派生类中“覆盖”基类的virtual函数,这里的“覆盖”是指virtual函数表的覆盖。当派生类的virtual函数的函数原型与其基类virtual函数的函数原型完全相同时,在派生类的virtual函数表中系统将使用派生类virtual函数覆盖其基类virtual函数。 例如: C+程序设计实例教程程序设计实例教程class Apublic: virtual void

10、 f1(); virtual void f2(int);class B:public Apublic: virtual void f1(); void f2(int);class C:public Bpublic: virtual void f1(); virtual int f3(); ;C+程序设计实例教程程序设计实例教程 上例中类型A、B和C的virtual函数表参见表8.48.6。 程序8.2演示了virtual函数的覆盖和动态多态的使用。C+程序设计实例教程程序设计实例教程1 /8.2 virtual函数的覆盖与多态函数的覆盖与多态 2 #include 3 using std:co

11、ut; 4 using std:endl; 5 class A 6 7 public: 8 virtual void f1() 9 10 coutvoid A:f1()被执行被执行endl;11 12 virtual void f2(int)13 14 coutvoid A:f2(int)被执行被执行endl;15 16 ;17 class B:public A18 19 public:20 virtual void f1()21 22 coutvoid B:f1()被执行被执行endl;23 C+程序设计实例教程程序设计实例教程24 void f2(int)25 26 coutvoid B:

12、f2(int)被执行被执行endl;27 28 ;29 class C:public B30 31 public:32 virtual void f1()33 34 coutvoid C:f1()被执行被执行endl;35 36 virtual int f3()37 38 coutint C:f3()被执行被执行endl;39 return 0;40 41 ;42 43 int main()44 45 A* p3;46 p0=new A;C+程序设计实例教程程序设计实例教程47 p1=new B;48 p2=new C;49 50 for(int i=0;if1();53 pi-f2(0);5

13、4 55 cout-f3();编译错误编译错误58 (C*)p2)-f3();59 cout-A:f1();62 p2-A:f1();63 /p2-B:f1();编译错误编译错误64 cout-endl;65 66 for( i=0;i3;+i)67 delete pi;68 return 0;69 显示结果:显示结果:void A:f1()被执行被执行void A:f2(int)被执行被执行void B:f1()被执行被执行void B:f2(int)被执行被执行void C:f1()被执行被执行void B:f2(int)被执行被执行-int C:f3()被执行被执行-void A:f1(

14、)被执行被执行void A:f1()被执行被执行-C+程序设计实例教程程序设计实例教程8.4 区分隐藏与区分隐藏与virtual函数覆盖函数覆盖 当成员函数为非virtual函数时,派生类和基类的同名成员函数一定是“隐藏”关系;当成员函数为virtual函数时,派生类和基类的同名成员函数可能是“隐藏”关系也可能是“覆盖”关系。当派生类和其基类中virtual函数的原型的返回值类型不同时,属于编译错误;参数列表不同,或是一个为const成员函数,另一个为非const成员函数时,属于“隐藏”关系。 程序8.3验证这一点。 C+程序设计实例教程程序设计实例教程5 class A 6 public:

15、8 virtual void f1() 9 coutvoid A:f1()被执行被执行endl;11 12 virtual void f2()13 coutvoid A:f2()被执行被执行endl;15 16 void f3()17 coutvoid A:f3()被执行被执行endl;19 20 virtual void f4()const21 coutvoid A:f4()被执行被执行endl;23 24 virtual void f5()25 coutvoid A:f5()被执行被执行endl;27 28 virtual void f6(int)29 coutvoid A:f6(int)

16、被执行被执行endl;31 32 virtual void f7(int=0)C+程序设计实例教程程序设计实例教程33 coutvoid A:f7(int)被执行被执行endl;35 36 virtual void f8()37 coutvoid A:f8()被执行被执行endl;39 ;41 class B:public A42 public:44 virtual void f1()45 coutvoid B:f1()被执行被执行endl;47 48 void f2()49 coutvoid B:f2()被执行被执行endl;51 52 virtual void f3()53 coutvoi

17、d B:f3()被执行被执行endl;55 56 virtual void f4()57 coutvoid B:f4()被执行被执行endl;59 60 virtual void f5()const61 coutvoid B:f5()被执行被执行endl; 显示结果:显示结果:类类A的构造函数被调用的构造函数被调用类类B的的1个参数的构造函数被调用个参数的构造函数被调用b1.x=0,b1.y=1类类B的析构函数被调用的析构函数被调用类类A的析构函数被调用的析构函数被调用-类类A的构造函数被调用的构造函数被调用类类B的的2个参数的构造函数被调用个参数的构造函数被调用b2.x=10,b2.y=20

18、类类B的析构函数被调用的析构函数被调用类类A的析构函数被调用的析构函数被调用-C+程序设计实例教程程序设计实例教程64 virtual void f6(char)65 coutvoid B:f6(char)被执行被执行endl;67 68 virtual void f7()69 coutvoid B:f7()被执行被执行endl;71 72 /*编译错误编译错误73 virtual int f8()74 coutvoid B:f8()被执行被执行f1();85 p-f2();86 p-f3();87 p-f4();88 p-f5();89 p-f6(A);90 p-f7();91 p-f8()

19、;92 delete p;93 return 0;94 显示结果:显示结果:void B:f1()被执行被执行void B:f2()被执行被执行void A:f3()被执行被执行void A:f4()被执行被执行void A:f5()被执行被执行void A:f6(int)被执行被执行void A:f7(int)被执行被执行void A:f8()被执行被执行 C+程序设计实例教程程序设计实例教程8.5 不能动态绑定函数参数的默认值不能动态绑定函数参数的默认值 当派生类的virtual函数覆盖其基类的virtual函数,并且这两个函数的参数均有默认值时,虽然系统将动态绑定函数体,但是参数的默认值

20、仍然静态绑定。也就是说,当“覆盖”发生时,指向派生类的基类指针或引用调用派生类版本的函数,但是如果函数参数取默认值调用时,参数值取基类版本中的默认值。 程序8.4验证这一点。C+程序设计实例教程程序设计实例教程5 class A 6 public: 8 virtual void f(int n=0) const 9 coutn=nendl;11 coutvoid A:f(int n=0)被执行被执行endl;12 14 ;15 class B:public A16 17 public:18 virtual void f(int n=10) const19 coutn=nendl;21 cout

21、void B:f(int n=10)被执行被执行f();29 delete p;30 return 0;31 显示结果:显示结果:n=0void B:f(int n=10)被执行被执行C+程序设计实例教程程序设计实例教程8.7 纯纯virtual函数与抽象类函数与抽象类 为了在继承结构中使用动态多态,我们需要在基类中声明virtual函数,并在派生类中覆盖它们。有时,基类的virtual函数存在的目的就是为了被派生类的同名virtual函数覆盖,而自己本身没有特别要实现的功能,同时,这种基类存在的目的仅仅为了派生其它类型,而本身不需要实例化。 程序8.6演示纯virtual函数的声明方法,并验

22、证抽象类不能实例化。C+程序设计实例教程程序设计实例教程1 /8.6 纯纯virtual函数与抽象类函数与抽象类 2 #include 3 using std:cout; 4 using std:endl; 5 6 class A 7 8 public: 9 virtual void f()const =0;/纯纯virtual函数函数10 ;11 12 class B1:public A13 14 virtual void f()15 16 coutB1:f()被执行被执行endl;17 18 ;19 20 class B2:public AC+程序设计实例教程程序设计实例教程21 22 p

23、ublic:23 virtual void f()const24 25 coutB2:f()被执行被执行f();38 delete p;39 return 0;40 显示结果:显示结果:B2:f()被执行被执行C+程序设计实例教程程序设计实例教程8.8 virtual析构函数 在派生类存在析构函数,并且在派生类对象释放时必须调用的情况下,我们需要将基类的析构函数设为virtual析构函数。 virtual析构函数的设置方法很简单,只需要在析构函数的声明前加入关键字virtual即可,基类的析构函数为virtual函数后,派生类的析构函数自动成为virtual析构函数,并且覆盖基类的析构函数。

24、程序8.7演示了virtual析构函数的使用方法。C+程序设计实例教程程序设计实例教程1 /8.7 virtual析构函数析构函数6 class A1 7 8 public: 9 A1()10 11 coutA1()被执行被执行endl;12 13 ;15 class B1:public A116 17 B1()18 19 coutB1()被执行被执行endl;20 21 ;23 class A224 25 public:C+程序设计实例教程程序设计实例教程26 virtual A2()27 coutA2()被执行被执行endl;29 30 ;32 class B2:public A233 3

25、4 B2()35 coutB2()被执行被执行endl;37 38 ;40 int main()41 A1 *p1=new B1;43 delete p1;44 cout-endl;46 A2 *p2=new B2;47 delete p2;48 cout-endl;49 return 0;50 显示结果:显示结果:A1()被执行被执行-B2()被执行被执行A2()被执行被执行-C+程序设计实例教程程序设计实例教程8.9 程序实例程序实例画图程序画图程序 画图程序是讲解动态多态的经典程序,本节的程序8.8中给出简单的画图程序,该程序中将实现“图形”基类Shape(抽象类),并且派生了“点”类P

26、oint、“直线”类Line和“圆”类Circle等3个具体类(与抽象类相反,能够实例化的类)。 本例中使用动态多态的方法处理各种“图形”的“画”方法,并结合纯virtual函数和virtual析构函数的知识。同时,由于本例的重点是继承结构中动态多态的实现方法,而不是真的要在输出设备上画图,因此,本例中的“画”方法仅仅是使用文字进行输出,而没有调用C+中关于图形显示的函数。C+程序设计实例教程程序设计实例教程8.10 RTTI 在继承结构中使用动态多态,我们总是使用基类指针指向派生类对象,这样可以以相同的方式处理不同派生类对象的类似问题。 但是,毕竟使用动态多态的继承结构中,基类指针指向的是不同类型的派生类对象,很有可能某个类的派生类对象存在特殊的问题需要处理 。 解决上述问题需要使用C+语言提供的RTTI机制,RTTI是指运行时的类型信息,即在运行时动态判断基类指针(或引用)到底指向的是哪一种派生类对象,以便做出正确的处理方法。 C+程序设计实例教程程序设计实例教程8.11 多继承结构中使用virtual析构函数 一个派生类同时拥有多个直接基类的情况称为多继承。在多继承的结构中,只要派生类诸多基类中的某一个存在必须被调用的析构函数,这些基类的析构函数都应该设为virtual析构函数。因为,在多继承的结构中,我们可能使用任意的基类指针指向派生类对象,在将该指针作为dele

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论