程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> VC++深入詳解:虛函數與多態性

VC++深入詳解:虛函數與多態性

編輯:vc教程

  因為魚的呼吸是吐泡泡,和一般動物的呼吸不太一樣,所以我們在fish類中重新定義breathe方法。我們希望如果對象是魚,就調用fish類的breathe()方法,如果對象是動物,那麼就調用animal類的breathe()方法。程序代碼如例2-16所示(EX08.CPP)。

  例2-16

  #include <iOStream.h>
  class animal
  {
  public:
     void eat()
     {
       cout<<"animal eat"<<endl;
     }
     void sleep()
     {
       cout<<"animal sleep"<<endl;
     }
     void breathe()
     {
       cout<<"animal breathe"<<endl;
     }
  };
  class fish:public animal
  {
  public:
     void breathe()
     {
       cout<<"fish bubble"<<endl;
     }
  };
  void fn(animal *pAn)
  {
     pAn->breathe();
  }
  void main()
  {
     animal *pAn;
     fish fh;
     pAn=&fh;
     fn(pAn);
  }

  我們在fish類中重新定義了breathe()方法,采用吐泡泡的方式進行呼吸。接著定義了一個全局函數fn(),指向animal類的指針作為fn()函數的參數。在main()函數中,定義了一個fish類的對象,將它的地址賦給了animal類的指針變量pAn,然後調用fn()函數。看到這裡,我們可能會有些疑惑,照理說,C++是強類型的語言,對類型的檢查應該是非常嚴格的,但是,我們將fish類的對象fh的地址直接賦給指向animal類的指針變量,C++編譯器居然不報錯。這是因為fish對象也是一個animal對象,將fish類型轉換為animal類型不用強制類型轉換,C++編譯器會自動進行這種轉換。反過來,則不能把animal對象看成是fish對象,如果一個animal對象確實是fish對象,那麼在程序中需要進行強制類型轉換,這樣編譯才不會報錯。

  讀者可以猜想一下例2-16運行的結果,輸出的結果應該是“animal breathe”,還是“fish bubble”呢?

  運行這個程序,你將看到如圖2.12所示的結果。

圖2.12 EX09程序的運行結果(一)

  為什麼輸出的結果不是“fish bubble”呢?這是因為在我們將fish類的對象fh的地址賦給pAn時,C++編譯器進行了類型轉換,此時C++編譯器認為變量pAn保存就是animal對象的地址。當在fn函數中執行pAn->breathe()時,調用的當然就是animal對象的breathe函數。

  為了幫助讀者更好地理解對象類型的轉換,我們給出了fish對象內存模型,如圖2.13所示。

  當我們構造fish類的對象時,首先要調用animal類的構造函數去構造animal類的對象,然後才調用fish類的構造函數完成自身部分的構造,從而拼接出一個完整的fish對象。當我們將fish類的對象轉換為animal類型時,該對象就被認為是原對象整個內存模型的上半部分,也就是圖2.13中的“animal的對象所占內存”。當我們利用類型轉換後的對象指針去調用它的方法時,自然也就是調用它所在的內存中的方法。因此,出現如圖2.12所示的結果,也就順理成章了。

  現在我們在animal類的breathe()方法前面加上一個virtual關鍵字,結果如例2-17所示。

  例2-17

  #include <iOStream.h>
  class animal
  {
  public:
     void eat()
     {
       cout<<"animal eat"<<endl;
     }
     void sleep()
     {
       cout<<"animal sleep"<<endl;
     }
virtual void breathe()
     {
       cout<<"animal breathe"<<endl;
     }
  };
  class fish:public animal
  {
  public:
     void breathe()
     {
       cout<<"fish bubble"<<endl;
     }
  };
  void fn(animal *pAn)
  {
     pAn->breathe();
  }
  void main()
  {
     animal *pAn;
     fish fh;
     pAn=&fh;
     fn(pAn);
  }


  用virtual關鍵字申明的函數叫做虛函數。運行例2-17這個程序,結果調用的是fish類的呼吸方法:

圖2.14 EX08程序的運行結果(二)

  這就是C++中的多態性。當C++編譯器在編譯的時候,發現animal類的breathe()函數是虛函數,這個時候C++就會采用遲綁定(late binding)技術。也就是編譯時並不確定具體調用的函數,而是在運行時,依據對象的類型(在程序中,我們傳遞的fish類對象的地址)來確認調用的是哪一個函數,這種能力就叫做C++的多態性。我們沒有在breathe()函數前加virtual關鍵字時,C++編譯器在編譯時就確定了哪個函數被調用,這叫做早期綁定(early binding)。

  C++的多態性是通過遲綁定技術來實現的,關於遲綁定技術,讀者可以參看相關的書籍,在這裡,我們就不深入講解了。

  C++的多態性用一句話概括就是:在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運行時將會根據對象的實際類型來調用相應的函數。如果對象類型是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved