Java和C++中多态的实现方式

http://www.kuqin.com/shuoit/20131201/336703.html

多态是面向对象的最主要的特性之一,是一种方法的动态绑定,实现运行时的类型决定对象的行为。多态的表现形式是父类指针或引用指向子类对象,在这个指针上调用的方法使用子类的实现版本。多态是IOC、模板模式实现的关键。

在C++中通过虚函数表的方式实现多态,每个包含虚函数的类都具有一个虚函数表(virtual table),在这个类对象的地址空间的最靠前的位置存有指向虚函数表的指针。在虚函数表中,按照声明顺序依次排列所有的虚函数。比如:

  1. classBase{
  2. public:
  3. virtualvoidf(){
  4. printf("Base::f()");
  5. }
  6.  
  7. virtualvoidg(){
  8. printf("Base::g()");
  9. }
  10. };
  11.  
  12. classDerived:publicBase{
  13. public:
  14. virtualvoidf(){
  15. printf("Derived::f()");
  16. }
  17. };

上面代码对应的类布局:

由于C++在运行时并不维护类型信息,所以在编译时直接在子类的虚函数表中将被子类重写的方法替换掉,如上图的Derived::f(),这个方法会被放到虚函数表中原来父函数在的位置。由于在编译时就确定了虚函数在虚表中的下标,所以在进行虚函数调用时,直接根据下标进行访问。比如,调用Derived对象上的f():

  1. Base*b=newDerived;
  2. b->f();

在调用b->f()时,内部会转化成(*b->vptr[1])(),由于虚函数表需要完成RTII,所以虚函数表的第一个slot存放的是type info,虚函数下标从1开始。实际上,虚函数表记录了这个类的所有虚函数的具体实现(就是在运行时确切要调用的),编译时就可以确定,不需要动态查找,效率较高。

而Java中,在运行时会维持类型信息以及类的继承体系。每一个类会在方法区中对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。其中,类型信息具有superclass属性指示了其超类,以及这个类对应的方法表(其中只包含这个类定义的方法,不包括从超类继承来的)。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。

JVM中用于方法调用的指令包括:

invokevirtual:用于调用实例方法,会根据对象的实际类型进行调用。

invokespecial:需要特殊处理的实例方法,比如:public final方法、私有方法和父类方法等。调用的方法取决于引用的类型。

invokeinterface:调用接口的方法。

invokestatic:调用类方法。

按照上面描述,对于子类覆盖父类的方法,编译后,调用指令应该是invokevirtual,调用的方法取决于对象的类型。invokevirtual方法查找的实现方式是:

id="google_ads_frame2" height="280" marginheight="0" src="http://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-9317413389774415&output=html&h=280&slotname=9618407928&adk=1081056908&w=336&lmt=1386336771&ea=0&flash=16.0.0.257&url=http%3A%2F%2Fwww.kuqin.com%2Fshuoit%2F20131201%2F336703.html&dt=1421375284988&shv=r20150113&cbv=r20141212&saldr=sb&prev_slotnames=9598016854&correlator=6329745435365&frm=20&ga_vid=481572065.1421375285&ga_sid=1421375285&ga_hid=910367814&ga_fc=0&u_tz=480&u_his=0&u_java=1&u_h=480&u_w=853&u_ah=455&u_aw=853&u_cd=32&u_nplug=0&u_nmime=0&dff=arial&dfs=14&adx=29&ady=2279&biw=837&bih=357&eid=317150304%2C828064101&oid=3&ref=http%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dutf-8%26f%3D8%26rsv_bp%3D1%26rsv_idx%3D1%26tn%3Dbaidu%26wd%3Djava%2520%25E5%25A4%259A%25E6%2580%2581%2520c%252B%252B%26rsv_pq%3D9d9ef54400099f12%26rsv_t%3D4cdeU6TyBtarMTJ2Kj%252BM3b011gzDmZx7fP43Gpri07H%252BwWA7atxl1kQyq8c%26rsv_enter%3D1%26rsv_sug3%3D12%26rsv_sug4%3D983%26rsv_sug1%3D7%26sug%3Djava%2520%25E5%25A4%259A%25E6%2580%2581%26oq%3Djava%2520%25E5%25A4%259A%25E5%258F%25B0%26rsv_n%3D1%26rsv_sug2%3D0%26inputT%3D11942&rx=0&eae=4&fc=24&docm=8&brdim=0%2C64%2C%2C%2C853%2C%2C%2C%2C%2C&vis=0&abl=XS&ppjl=u&pfx=0&fu=1032&bc=0&ifi=2&dtd=82" frameborder="0" width="336" allowtransparency="" name="google_ads_frame2" marginwidth="0" scrolling="no" allowfullscreen="true" _xhe_src="http://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-9317413389774415&output=html&h=280&slotname=9618407928&adk=1081056908&w=336&lmt=1386336771&ea=0&flash=16.0.0.257&url=http%3A%2F%2Fwww.kuqin.com%2Fshuoit%2F20131201%2F336703.html&dt=1421375284988&shv=r20150113&cbv=r20141212&saldr=sb&prev_slotnames=9598016854&correlator=6329745435365&frm=20&ga_vid=481572065.1421375285&ga_sid=1421375285&ga_hid=910367814&ga_fc=0&u_tz=480&u_his=0&u_java=1&u_h=480&u_w=853&u_ah=455&u_aw=853&u_cd=32&u_nplug=0&u_nmime=0&dff=arial&dfs=14&adx=29&ady=2279&biw=837&bih=357&eid=317150304%2C828064101&oid=3&ref=http%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dutf-8%26f%3D8%26rsv_bp%3D1%26rsv_idx%3D1%26tn%3Dbaidu%26wd%3Djava%2520%25E5%25A4%259A%25E6%2580%2581%2520c%252B%252B%26rsv_pq%3D9d9ef54400099f12%26rsv_t%3D4cdeU6TyBtarMTJ2Kj%252BM3b011gzDmZx7fP43Gpri07H%252BwWA7atxl1kQyq8c%26rsv_enter%3D1%26rsv_sug3%3D12%26rsv_sug4%3D983%26rsv_sug1%3D7%26sug%3Djava%2520%25E5%25A4%259A%25E6%2580%2581%26oq%3Djava%2520%25E5%25A4%259A%25E5%258F%25B0%26rsv_n%3D1%26rsv_sug2%3D0%26inputT%3D11942&rx=0&eae=4&fc=24&docm=8&brdim=0%2C64%2C%2C%2C853%2C%2C%2C%2C%2C&vis=0&abl=XS&ppjl=u&pfx=0&fu=1032&bc=0&ifi=2&dtd=82">

1.通过对象中类指针找到其类信息,然后在方法表中根据方法签名找到该方法。

2. 如果不在当前类,则递归查找其父类的方法表直到Object类。

3. 如果找到Object类,也没有该方法,会抛出NoSuchMethodException异常。

与js、lua等动态语言类似,Java的实现方式依赖于内存中的类型体系信息,存在一个“原型链”,是一个完全动态的查找过程,相对于C++而言,效率会低一些,因为存在一个链表遍历查找的过程。之所以,Java中可以这样实现,本质上是因为它是一门虚拟机语言,虚拟机会维持所有的这些类型信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值