程式扎記: [ C++ Gossip ] 物件導向 : 多型 - 虛擬函式(Virtual function)

標籤

2014年8月9日 星期六

[ C++ Gossip ] 物件導向 : 多型 - 虛擬函式(Virtual function)

來源自 這裡 
Preface 
之前曾經介紹過函式與運算子的重載Overload),重載可以使用一個函式名稱來執行不同的實作,這是一種「編譯時期」就需決定的方式,這是「早期繫 結」(Early binding)、「靜態繫結」(Static binding),因為在編譯時就可以決定函式的呼叫對象,它們的呼叫位址在編譯時就可以得知。 

Virtual Function 
「虛擬函式」(Virtual function)可以實現「執行時期」的多型支援,是一個「晚期繫結」(Late binding)、「動態繫結」(Dynamic binding),必須在執行時期才會得知所要調用的物件或其上的公開介面。一個基底類別的物件指標,可以用來指向其衍生類別物件而不會發生錯誤,例如若基底類別是 Foo1,而衍生類別是 Foo2,則 下面這個指定是可以接受的: 
  1. Foo1 *fptr;   
  2. Foo2 f2;   
  3. fptr = &f2;  
多型與動態繫結的基礎從這開始,它們只有在使用指標或參考時才得以發揮它們的特性,然而由於 fptr 仍是 Foo1 類型的指標,它只能存取 Foo1 中有定義 的成員,目前來說也只能操作 Foo1 中的成員。注意將衍生類別型態的指標指向基底類別的物件基本是不可行的雖然可以使用型態轉換的方式來勉強達成,但並不鼓勵),衍生類別的指標並不能存取基底類別的 成員。 

虛擬函式是一種成員函式,它在基底類別中使用關鍵字 "virtual" 宣告(定義),並在衍生類別中重新定義虛擬函式,這將成員函式的操作決議 (Resolution)推遲至執行時期再決定。 

虛擬函式可以實現執行時期的「多型」,也就是「一個介面,多種函式」,一個含有虛擬函式的類別被稱為「多型的類別」(Polymorphic class),當一個基底類別型態的指標指向一個含有虛擬函式的衍生類別,您就可以使用這個指標來存取衍生類別中的虛擬函式,下面這個例子是個簡單的示 範: 
- testVirtualFunc.cpp 
  1. #include   
  2. using namespace std;  
  3.   
  4. class Foo1 {  
  5. public:  
  6.   virtual void show() { // 虛擬函式  
  7.     cout << "Foo1's show" << endl;  
  8.   }  
  9. };  
  10.   
  11. class Foo2 : public Foo1 {  
  12. public:  
  13.   virtual void show() { // 虛擬函式  
  14.     cout << "Foo2's show" << endl;  
  15.   }  
  16. };  
  17.   
  18. void showFooByPtr(Foo1 *foo) {  
  19.   foo->show();  
  20. }  
  21.   
  22. void showFooByRef(Foo1 &foo) {  
  23.   foo.show();  
  24. }  
  25.   
  26. int main() {  
  27.   Foo1 f1;  
  28.   Foo2 f2;  
  29.   Foo1 *f1Ptr=&f2;  
  30.   // 動態繫結  
  31.   cout << "\t[Info] Show Foo By Ptr:" << endl;  
  32.   showFooByPtr(&f1);  
  33.   showFooByPtr(&f2);  
  34.   showFooByPtr(f1Ptr);  
  35.   cout << endl;  
  36.   
  37.   // 動態繫結  
  38.   cout << "\t[Info] Show Foo By Ref:" << endl;  
  39.   showFooByRef(f1);  
  40.   showFooByRef(f2);  
  41.   cout << endl;  
  42.   
  43.   
  44.   // 靜態繫結  
  45.   cout << "\t[Info] Show Foo By Object:" << endl;  
  46.   f1.show();  
  47.   f2.show();  
  48.   cout << endl;  
  49.   
  50.   return 0;  
  51. }  
執行結果: 
[Info] Show Foo By Ptr:
Foo1's show
Foo2's show
Foo2's show

[Info] Show Foo By Ref:
Foo1's show
Foo2's show

[Info] Show Foo By Object:
Foo1's show
Foo2's show
如果把 Foo1 的 virtual 拿掉, 則執行結果為: 
[Info] Show Foo By Ptr:
Foo1's show

Foo1's show
Foo1's show


[Info] Show Foo By Ref:
Foo1's show

Foo1's show

[Info] Show Foo By Object:
Foo1's show
Foo2's show

showFooByPtr() 與 showFooByRef() 函式並無法事先知道要操作的是哪一個物件的哪一個公開介面,最後的操作要在執行時期才能決 定。衍生類別中重新定義虛擬函式時,virtual 可以根據需求加上,如果再接下來的衍生類別仍想進行多型操作,則加上 virtual ,如果不打算進行多型操 作,則可以不加上。

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!