自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C++和java多態(tài)的區(qū)別

開發(fā) 開發(fā)工具
C++和java都具多態(tài)性,多態(tài)性其實(shí)就是方法調(diào)用的機(jī)制,也就是說當(dāng)在編譯時(shí)無法確定一個(gè)對象的實(shí)際類型時(shí),應(yīng)當(dāng)能夠在運(yùn)行時(shí)基于對象的實(shí)際類型來決定調(diào)用的具體方法。本文主要介紹C++和java多態(tài)的區(qū)別。

  以前我有個(gè)錯(cuò)誤的觀點(diǎn):即使在C++java多態(tài)性的實(shí)現(xiàn)機(jī)制可能不同,但它們的表現(xiàn)形式應(yīng)該相同,也就是說如果代碼結(jié)構(gòu)相同,那么執(zhí)行結(jié)果也應(yīng)該相同??上屡c愿違,事情并不總是我想象中的那樣子,那么C++和java多態(tài)到底有何區(qū)別呢?

  首先我們提一下多態(tài)性的概念。根據(jù)Bjarne Stoustrup的說法,多態(tài)性其實(shí)就是方法調(diào)用的機(jī)制,也就是說當(dāng)在編譯時(shí)無法確定一個(gè)對象的實(shí)際類型時(shí),應(yīng)當(dāng)能夠在運(yùn)行時(shí)基于對象的實(shí)際類型來決定調(diào)用的具體方法(動(dòng)態(tài)綁定)。

  我們先來看一下在C++中的函數(shù)調(diào)用方式:

  • 普通函數(shù)調(diào)用:具體調(diào)用哪個(gè)方法在編譯時(shí)間就可以決定(通過查找編譯器的符號表),同時(shí)在使用標(biāo)準(zhǔn)過程調(diào)用機(jī)制基礎(chǔ)上增加一個(gè)表示對象身份的指針(this指針)。
  • 虛函數(shù)調(diào)用:函數(shù)調(diào)用依賴于對象的實(shí)際類型,一般地說,對象的實(shí)際類型只能在運(yùn)行時(shí)間才能確定。虛函數(shù)一般要有兩個(gè)步驟來支持,首先每一個(gè)類產(chǎn)生出一堆指向虛函數(shù)的指針,放在表格中,這個(gè)表格就叫虛函數(shù)表(virtual table);然后每一個(gè)類對象(class object)會添加一個(gè)指向相關(guān)虛函數(shù)表(virtual table)的指針,通常這個(gè)指針叫做vptr。

  在java中又是如何的呢?恩,區(qū)別還是滿大的。在java虛擬機(jī)中,類實(shí)例的引用就是指向一個(gè)句柄(handle)的指針,而該句柄(handle)其實(shí)是一對指針:其中一個(gè)指針指向一張表,該表格包含了對象的方法列表以及一個(gè)指向類對象(表示對象類型)的指針;另一個(gè)指針指向一塊內(nèi)存地址,該內(nèi)存是從java堆中為對象的數(shù)據(jù)而分配出來的。

  這時(shí),你可能會說,好象差不多嘛,不是都要維護(hù)一張函數(shù)表嗎?別急,讓我們先看一下例子,這樣你就能更好的理解它們之間的區(qū)別到底有多大了。

  下面是C++和java的例子,不看后面的答案,你能夠正確說出它們的執(zhí)行結(jié)果嗎?

  例1:C++

  1.   class Base   
  2.   {   
  3.   public:   
  4.   Base()   
  5.   {   
  6.   init();   
  7.   }   
  8.   virtual ~Base() {}   
  9.   public:   
  10.   virtual void do_init()   
  11.   {   
  12.   init();   
  13.   }   
  14.   protected:   
  15.   virtual void init()   
  16.   {   
  17.   cout << "in Base::init()" << endl;   
  18.   }   
  19.   };   
  20.   class Derived : public Base   
  21.   {   
  22.   public:   
  23.   Derived()   
  24.   {   
  25.   init();   
  26.   }   
  27.   protected:   
  28.   void init()   
  29.   {   
  30.   cout << "in Derived::init()" << endl;   
  31.   }   
  32.   };   
  33.   int main(int argc, char* argv[])   
  34.   {   
  35.   Base* pb;   
  36.   pb = new Derived();   
  37.   delete pb;   
  38.   return 0;   
  39.   }      

  例2:java

  1. class Base   
  2.   {   
  3.   public Base()   
  4.   {   
  5.   init();   
  6.   }   
  7.   protected void init()   
  8.   {   
  9.   System.out.println("in Base::init()");   
  10.   }   
  11.   public void do_init()   
  12.   {   
  13.   init();  
  14.    }  
  15.    }   
  16.   class Derived extends Base   
  17.   {   
  18.   public Derived()   
  19.   {   
  20.   init();   
  21.   }   
  22.   protected void init()   
  23.   {   
  24.   System.out.println("in Derived::init()");  
  25.   }   
  26.   }   
  27.   public class Test   
  28.   {   
  29.   public static void main(String[] args)   
  30.   {   
  31.   Base base = new Derived();   
  32.   }   
  33.   }      

  例1的執(zhí)行結(jié)果是:

  1. in Base::init()   
  2. in Derived::init()      

  例2的執(zhí)行結(jié)果是:

  1. in Derived::init()   
  2. in Derived::init()  
  3.      

  看了結(jié)果后,你是馬上頓悟呢抑或是處于疑惑中呢?ok,我們來分析一下兩個(gè)例子的執(zhí)行過程。

  首先看一下例1(C++的例子):

  1. Base* pb; 只是聲明,不做什么。

  2. pb = new Derived();

  1) 調(diào)用new操作符,分配內(nèi)存。

  2) 調(diào)用基類(本例中是Base)的構(gòu)造函數(shù)

  3) 在基類的構(gòu)造函數(shù)中調(diào)用init(),執(zhí)行程序首先判斷出當(dāng)前對象的實(shí)際類型是Base(Derived還沒構(gòu)造出來,當(dāng)然不會是Derived),所以這里調(diào)用的是Base::init()。

  4) 調(diào)用派生類(本例中是Derived)的構(gòu)造函數(shù),在這里同樣要調(diào)用init(),執(zhí)行程序判斷出當(dāng)前對象的實(shí)際類型是Derived,調(diào)用Derived::init()。

  3. delete pb; 無關(guān)緊要。

  例2(java的例子)的執(zhí)行過程:

  1. Base base = new Derived();

  1) 分配內(nèi)存。

  2) 調(diào)用基類(本例中是Base)的構(gòu)造函數(shù)

  3) 在基類的構(gòu)造函數(shù)中調(diào)用init(),執(zhí)行程序首先判斷出當(dāng)前對象的實(shí)際類型是Derived(對,Derived已經(jīng)構(gòu)造出來,它的函數(shù)表當(dāng)然也已經(jīng)確定了)所以這里調(diào)用的是Derived::init()。

  4) 調(diào)用派生類(本例中是Derived)的構(gòu)造函數(shù),在這里同樣要調(diào)用init(),執(zhí)行程序判斷出當(dāng)前對象的實(shí)際類型是Derived,調(diào)用Derived::init()。

  明白了吧。java中的類對象在構(gòu)造前(調(diào)用構(gòu)造函數(shù)之前)就已經(jīng)存在了,其函數(shù)表和對象類型也已經(jīng)確定了,就是說還沒有出生就已經(jīng)存在了。而C++中只有在構(gòu)造完畢后(所有的構(gòu)造函數(shù)都被成功調(diào)用)才存在,其函數(shù)表和對象的實(shí)際類型才會確定。所以這兩個(gè)例子的執(zhí)行結(jié)果會不一樣。當(dāng)然,構(gòu)造完畢后,C++與java的表現(xiàn)就都一樣了,例如你調(diào)用Derived::do_init()的話,其執(zhí)行結(jié)果是:

  1.   in Derived::init() 

  個(gè)人認(rèn)為,java中的多態(tài)實(shí)現(xiàn)機(jī)制沒有C++中的好。還是以例子說明吧:

  例子3:C++

  1.   class Base  
  2.   {  
  3.   public:  
  4.   Base()  
  5.   {  
  6.   init();  
  7.   }  
  8.   virtual ~Base() {}  
  9.   protected:  
  10.   int value;  
  11.   virtual void init()  
  12.   {  
  13.   value = 100;  
  14.   }  
  15.   };  
  16.   class Derived : public Base  
  17.   {  
  18.   public:  
  19.   Derived()  
  20.   {  
  21.   init();  
  22.   }  
  23.   protected:  
  24.   void init()  
  25.   {  
  26.   cout << "value = " << value << endl;  
  27.   // 做一些額外的初始化工作  
  28.   }  
  29.   };  
  30.   int main(int argc, char* argv[])  
  31.   {  
  32.   Base* pb;  
  33.   pb = new Derived();  
  34.   delete pb;  
  35.   return 0;  
  36.   } 

  例4:java

  1.   class Base   
  2.   {   
  3.   public Base()   
  4.   {   
  5.   init();   
  6.   }   
  7.   protected int value;   
  8.   protected void init()   
  9.   {   
  10.   value = 100;   
  11.   }   
  12.   }   
  13.   class Derived extends Base   
  14.   {   
  15.   public Derived()   
  16.   {   
  17.   init();   
  18.   }   
  19.   protected void init()   
  20.   {   
  21.   System.out.println("value = " + value);   
  22.   // 做一些額外的初始化工作   
  23.   }   
  24.   }   
  25.   public class Test   
  26.   {   
  27.   public static void main(String[] args)   
  28.   {   
  29.   Base base = new Derived();   
  30.   }   
  31.   }  

  例3的執(zhí)行結(jié)果是:

  1.   value = 10 

  例4的執(zhí)行結(jié)果是:

  1.   value = 0 
  2.   value = 0 

  從以上結(jié)果可以看出,java例子中應(yīng)該被初始化的值(這里是value)沒有被初始化,派生類根本不能重用基類的初始化函數(shù)。試問,如果初始化要在構(gòu)造時(shí)完成,并且初始化邏輯比較復(fù)雜,派生類也需要額外的初始化,派生類是不是需要重新實(shí)現(xiàn)基類的初始化函數(shù)呢?這樣的面向?qū)ο蠓椒ê貌缓媚?歡迎大家討論。

【編輯推薦】

  1. 實(shí)例演示C++多態(tài)的實(shí)現(xiàn)過程
  2. C++多態(tài)實(shí)現(xiàn)方法探討
  3. C++多態(tài)性基本概念講述
  4. 深入理解Java多態(tài)性
  5. 對Java程序多態(tài)性支持的改進(jìn)
  6. 深入Java核心 Java中多態(tài)的實(shí)現(xiàn)機(jī)制
責(zé)任編輯:韓亞珊 來源: 天極網(wǎng)開發(fā)頻道
相關(guān)推薦

2011-04-12 10:40:04

C++多態(tài)

2011-04-11 09:43:25

C++C

2011-07-15 00:47:13

C++多態(tài)

2011-06-21 15:00:07

JAVAC++

2011-12-25 15:35:05

ibmdwJavaC++

2024-04-29 07:48:04

C++FinalOverride

2010-01-28 14:38:36

C++和C#、Java

2020-06-17 12:22:44

C覆蓋重載

2022-07-01 11:56:54

C語言C++編程語言

2010-01-08 16:52:57

C++和C#

2010-02-03 10:50:33

C++多態(tài)

2010-11-22 16:01:08

C++多態(tài)

2010-01-28 15:22:12

C++嵌套類

2024-04-22 13:22:00

虛函數(shù)象編程C++

2024-01-23 10:13:57

C++虛函數(shù)

2009-10-22 09:17:16

C++ CLR

2011-07-13 18:00:51

CC++VC

2024-06-28 10:04:09

2024-02-26 12:13:32

C++開發(fā)編程

2010-02-05 16:07:52

C++多態(tài)覆蓋
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號