虚函数怎么实现多态.doc
上传人:sy****28 上传时间:2024-09-12 格式:DOC 页数:3 大小:33KB 金币:16 举报 版权申诉
预览加载中,请您耐心等待几秒...

虚函数怎么实现多态.doc

虚函数怎么实现多态.doc

预览

在线预览结束,喜欢就下载吧,查找使用更方便

16 金币

下载此文档

如果您无法下载资料,请参考说明:

1、部分资料下载需要金币,请确保您的账户上有足够的金币

2、已购买过的文档,再次下载不重复扣费

3、资料包下载后请先用软件解压,在使用对应软件打开

虚函数如何实现多态虚函数联系到多态,多态联系到继承。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。下面来看一段简单的代码classA{public:voidprint(){cout<<”ThisisA”<<endl;}};classB:publicA{public:voidprint(){cout<<”ThisisB”<<endl;}};intmain(){//为了在以后便于区分,我这段main()代码叫做main1Aa;Bb;a.print();b.print();}通过classA和classB的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,输出的结果也是我们预料中的,分别是ThisisA和ThisisB。但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。那现在就把main()处的代码改一改。intmain(){//main2Aa;Bb;A*p1=&a;A*p2=&b;p1->print();p2->print();}运行一下看看结果,结果却是两个ThisisA。问题来了,p2明明指向的是classB的对象但却是调用的classA的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数。classA{public:virtualvoidprint(){cout<<”ThisisA”<<endl;}//现在成了虚函数了};classB:publicA{public:voidprint(){cout<<”ThisisB”<<endl;}//这里需要在前面加上关键字virtual吗?};毫无疑问,classA的成员函数print()已经成了虚函数,那么classB的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,classB的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。现在重新运行main2的代码,这样输出的结果就是ThisisA和ThisisB了。现在来消化一下,我作个简单的总结,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。虚函数是如何做到的虚函数是如何做到因对象的不同而调用其相应的函数的呢?现在我们就来剖析虚函数。我们先定义两个类classA{//虚函数示例代码public:virtualvoidfun(){cout<<1<<endl;}virtualvoidfun2(){cout<<2<<endl;}};classB:publicA{public:voidfun(){cout<<3<<endl;}voidfun2(){cout<<4<<endl;}};由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。那段数据叫做vptr指针,指向那个表。那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址,请看图:通过上图,可以看到这两个vtbl分别为classA和classB服务。现在有了这个模型之后,我们来分析下面的代码:A*p=newA;p->fun();毫无疑问,调用了A::fun(),但是A::fun()是如何被调用的呢?它像普通函数那样直接跳转到函数的代码处吗?No,其实是这样的,首先是取出vptr的值,这个值就是vtbl的地址,再根据这个值来到vtbl这里,由于调用的函数A::fun()是第一个虚函数,所以取出vtbl第一个slot里的值,这个值就是A::fun()的地址了,最后调用这个函数。现在我们可以看出来了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里装着对应类的虚函数地址,所以这样虚函数就可以完成它的任务。子类重写的虚函数的地址直接替换了父类虚函数在虚表中的位置,因此当访问虚函数时,该虚表中的函数是谁的就访问谁。而对于classA和classB来说,他们的vptr指针存放在何处呢?其实这个指针就放在他们各自的实例对象里。由于classA和classB都没有数据成员,所以他们的实例对象里就只有一个vptr指针