2010年9月16日 星期四

[C++ 小學堂] C++ : Virtual 函數定義


虛函數的定義很簡單, 只要在成員函數原型前加上一個virtual 關鍵字即可. 如果一個父類被宣告為virtual函數, 其所有子類 (含子類的子類) 會保持virtual 特性. 一旦一個函數被宣告為虛函數, 不管繼承幾次, virtual特性將維持. 即使在子類沒使用virtual 關鍵字, 其仍是Virtual 函數.
子類可根據需求對Virtual函數重新定義, 重新定義的挌式有一定要求:
* 其參數類型與父類一致.
* 其參數個數一致.
* 返回類型需與父類相同或是為父類返回類型的子類. 並且如果父類返回引用或指針, 也需一至.

Ps. 友元函數不能是虛函數, 因為友元函數不是類成員. 但虛函數可以是另一個類的友元函數.

指針訪問
使用指針訪問虛函數, 與指針類型無關, 編譯器會根據指針所指類型來實現動態聯編.

引用訪問
使用引用訪問虛函數與使用指針訪問虛函數類似, 不同的是引用已經聲明, 不能修改. 但這在一定程度上提高了代碼的安全性, 特別體現在函數參數的傳遞. 可以將引用視為一種 "受限制的指針", 請參考如下範例:

範例代碼 Base.h:
  1. #ifndef _BASE  
  2. #define _BASE  
  3.   
  4. class Base{  
  5. public:  
  6.     virtual void disp(){  
  7.         cout << "Hello, Base " <
  8.     }  
  9. };  
  10.   
  11. class Child:public Base{  
  12. public:  
  13.     void disp(){  
  14.         cout << "Hello, Child " << endl;  
  15.     }  
  16. };  
  17.   
  18. #endif  

範例代碼 example_ch11.h:
  1. ...(以上省略)...  
  2. void example1104(){  
  3.     cout << "@ 使用引用訪問Virtual函數 (P273)" << endl;  
  4.     Base obj_Base;  
  5.     Child obj_Child;  
  6.     cout << "Base& rBase1 = obj_Base;" << endl;  
  7.     Base& rBase1 = obj_Base;  
  8.     rBase1.disp();  
  9.     cout << "Base& rBase2 = obj_Child;" << endl;  
  10.     Base& rBase2 = obj_Child;  
  11.     rBase2.disp();  
  12. }  
  13. ...(以下省略)...  

結果:
@ 使用引用訪問Virtual函數 (P273)
Base& rBase1 = obj_Base;
Hello, Base
Base& rBase2 = obj_Child;
Hello, Child

由如上代碼得知, 引用訪問虛函數同樣與引用類型無關, 只取決於引用初始化的對象.

類內訪問
編譯器對於使用類對像名進行的虛函數調用是使用靜態聯編, 而且使用諸如 "類名::虛函數" 等使用了作用域運算符亦使用靜態聯編. 此時具體調用哪個函數取決於類名. 如下範例 "void call_base_1()" 中對虛函數的調用是使用動態聯編, 關鍵在於 this 指針的應用.
範例代碼 Base1105.h:
  1. #ifndef _BASE_1105  
  2. #define _BASE_1105  
  3.   
  4. class Base1105{  
  5. public:  
  6.     virtual void disp(){  
  7.         cout << "Hello, Base" << endl;  
  8.     }  
  9.   
  10.     void call_base_1(){  
  11.         cout << "  this->disp();" << endl;  
  12.         this->disp();  
  13.     }  
  14.   
  15.     void call_base_2(){  
  16.         cout << "  Base1105::disp();" << endl;  
  17.         Base1105::disp();  
  18.     }  
  19. };  
  20.   
  21. class Child1105:public Base1105{  
  22. public:  
  23.     void disp(){  
  24.         cout << "Hello, Child" << endl;  
  25.     }  
  26.   
  27.     void call_child_1(){  
  28.         disp();  
  29.     }  
  30. };  
  31.   
  32. #endif  

範例代碼 example_ch11.cpp:
  1. ...(以上省略)...  
  2. void example1105(){  
  3.     showMessage("@ 類內成員函數訪問Virtual函數 (P274)");  
  4.     Base1105 obj_Base;  
  5.     Child1105 obj_Child;  
  6.     cout << "  obj_Base.call_base_1();" << endl;  
  7.     obj_Base.call_base_1();  
  8.     cout << "  obj_Child.call_child_1();" << endl;  
  9.     obj_Child.call_child_1();  
  10.     cout << "  Base1105* pBase=&obj_Base;" << endl;  
  11.     Base1105* pBase=&obj_Base;  
  12.     cout << "  pBase->call_base_1();" << endl;  
  13.     pBase->call_base_1();  
  14.     cout << "  pBase->call_base_2();" << endl;  
  15.     pBase->call_base_2();  
  16.   
  17.     cout << "  pBase = &obj_Child;" << endl;  
  18.     pBase = &obj_Child;  
  19.     cout << "  pBase->call_base_1();" << endl;  
  20.     pBase->call_base_1();  
  21.     cout << " pBase->call_base_2();" << endl;  
  22.     pBase->call_base_2();      
  23. }  
  24. ...(以下省略)...  

結果:
Message: @ 類內成員函數訪問Virtual函數 (P274)
obj_Base.call_base_1();
this->disp();
Hello, Base
obj_Child.call_child_1();
Hello, Child
Base1105* pBase=&obj_Base;
pBase->call_base_1();
this->disp();
Hello, Base
pBase->call_base_2();
Base1105::disp();
Hello, Base
pBase = &obj_Child;
pBase->call_base_1();
this->disp();
Hello, Child
pBase->call_base_2();
Base1105::disp();
Hello, Base


在構造函數或解構函數中訪問
構造函數和解構函數是特殊的成員函數, 在其中訪問虛函數時, C++ 採用靜態聯編. 請參考如下範例:
範例代碼 Base.h:
  1. class Base{  
  2. public:  
  3.   virtual void disp(){  
  4.     cout << "Hello, base" << endl;  
  5.   }  
  6. };  
  7.   
  8. class Child:public Base{  
  9. public:  
  10.   Child(){  
  11.     disp();  
  12.   }  
  13.   disp(){  
  14.     cout << "Hello, child" << endl;  
  15.   }  
  16. };  

上述代碼定義創建類Child時, 輸出訊息會是"Hello, child", 換言之在Child的構造函數中, 不論是用disp() 還是 this->disp()來調用, 編譯器都會將之解釋為 Child::disp(), 此時若想在構造函數調用Base類的disp函數, 必須使用作用域運算符, 即Base::disp() 型式.
This message was edited 5 times. Last update was at 27/01/2010 15:10:25

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...