標籤:記憶體 clu 儲存空間 派生 條件 turn play .net code
參考:http://www.weixueyuan.net/view/6370.html
總結:
而多態的功能則是將函數名動態綁定到函數入口地址,這樣的動態綁定過程稱為運行期綁定。
而在運行期綁定的函數我們稱其是多態的。
通過基類類型的指標根據所指向對象的類型來自動決定調用基類還是衍生類別的display函數
要想形成多態必須具備以下三個條件:
- 必須存在繼承關係;
- 繼承關係中必須有同名的虛函數;
- 存在基類類型的指標或引用,通過該指標或引用調用虛函數。
在C++程式中,程式的每一個函數在記憶體中會被分配一段儲存空間,而被分配的儲存空間的起始地址則為函數的入口地址。例如我們在設計一個程式時都必須為程式設計一個主函數,主函數同樣會在記憶體中被分配一段儲存空間,這段儲存空間的起始地址即為函數的入口地址。
在前面的所有列舉的程式中,函數的入口地址與函數名是在編譯時間進行綁定的,我們稱之為編譯期綁定,而多態的功能則是將函數名動態綁定到函數入口地址,這樣的動態綁定過程稱為運行期綁定,換句話來說就是函數名與函數入口地址在程式編譯時間無法綁定到一起,只有等啟動並執行時候才確定函數名與哪一個函數入口綁定到一起。
那麼多態到底有什麼用處呢?我們不妨來看個例子。在windows作業系統中,我們經常會進行一些關閉操作,比如關閉檔案夾、關閉文字檔、關閉播放器視窗等,這些關閉動作對應的函數close假設都繼承自同一個基類,但是每一個類都需要有自己的一些特殊功能,比如清理背景、清除緩衝等工作,如此一來當我們執行close函數時,我們當然希望根據當前所操作的視窗類別型來決定該執行哪一個close函數,因此運行期綁定就可以派上用場了。
編譯期綁定是指在程式編譯時間就將函數名與函數入口地址綁定到一起,運行期綁定是指在程式運行時才將函數名與函數入口地址綁定到一起,而在運行期綁定的函數我們稱其是多態的。
為了說明虛函數的必要性,我們先來看一個樣本程式。
例1:
#include<iostream>using namespace std;class base{public: void display(){cout<<"I‘m base class!"<<endl;}};class derived: public base{public: void display(){cout<<"I‘m derived class!"<<endl;}};int main(){ base * p; derived test; p = &test; p->display(); return 0;}
這個例子非常簡單,兩個類,一個是base類,一個是derived類,二者構成繼承關係,同時在這兩個類中均含有一個display函數,因為函數同名,故在衍生類別對象中會出現遮蔽現象,即衍生類別中的display函數會遮蔽基類中的display函數。
在主函數中,定義了一個基類類型的指標p和衍生類別對象test,之後p指標指向衍生類別對象test,然後通過指標調用display函數。此程式最終運行結果如下:
I’m base class!
從結果來看這個程式最終調用的display函數是基類的display函數,而非衍生類別中的display函數。但此程式的本意是先通過基類類型的指標根據所指向對象的類型來自動決定調用基類還是衍生類別的display函數。為了實現這樣的一種功能,C++提供了多態這一機制。
要想形成多態必須具備以下三個條件:
- 必須存在繼承關係;
- 繼承關係中必須有同名的虛函數;
- 存在基類類型的指標或引用,通過該指標或引用調用虛函數。
根據這三個條件,我們將例1進行修改,使display函數具有多態特性。修改後程式如例2 所示。
例2:
#include<iostream>using namespace std;class base{public: virtual void display(){cout<<"I‘m base class!"<<endl;}};class derived: public base{public: virtual void display(){cout<<"I‘m derived class!"<<endl;}};int main(){ base * p; derived test; p = &test; p->display(); return 0;}
例2所示程式相對於例1隻是在display函數前各添加了一個virtual關鍵字。我們對照三個多態的構成條件來分析一下,多態首先需要繼承關係,derived類繼承自base類,因此base類和derived類構成繼承關係;其次多態需要同名的虛函數,base類和derived類中都有display函數,同名滿足,同時通過添加關鍵字virtual後,display函數成為虛函數;最後多態需要通過基類類型的指標或引用來調用虛函數,在例2中的主函數中,p即為基類類型指標,並且將該指標指向衍生類別對象,然後調用display函數。這段程式最終運行結果如下:
I’m derived class!
例2這個程式展示出來的就是多態,display函數通過virtual關鍵字聲明為虛函數,具有多態特性。我們將例2中的主函數修改成以下形式再來分析一下函數運行結果。
複製格式化新視窗
int main(){ base * p = new base; p->display(); delete p; p = new derived; p->display(); delete p; return 0;}
在這個主函數中同樣是聲明一個基類類型的指標,之後通過new給指標分配一個基類對象,通過p指標調用display函數,此時不用說肯定是輸出“I’m base class!”,因為這中間一直沒有涉及到衍生類別的事情。之後銷毀之前new分配的base對象,然後通過new分配一個derived類對象,p指向該衍生類別對象,通過p指標調用display函數,此時的情況和例2是完全相同的,因此輸出“I’m derived class!”,之後再delete銷毀衍生類別對象。修改主函數之後程式輸出結果如下:
I’m base class!
I’m derived class!
這樣的輸出結果與我們的分析結果是一致的。
4.1 C++多態的概念及前提條件