數據封裝、繼承與多態性,是麵向對象程序設計的三大特色。數據封裝是將類中的成員按其被使用或存取的方式分類。繼承可以清晰自然地表達實際問題中的分類結構或層次結構。多態勝則將麵向對象程序設計推向一個更高的境界。
Visual C++支持兩種多態性:編譯時的多態性和運行時的多態性。編譯時的多態性通過使用函數重載和運算符重載獲得,運行時的多態性通過使用虛函數來實現。
9.1動態連接與靜態連接
編譯器在編譯時便確定要做的事情(如某個已知函數的調用),稱為靜態連接。
雖然程序中利用switch語句,依情況選擇應該執行的函數,但對於每一種情況,編譯器在編譯時便已經得知應調用哪一個函數,因為在程序中已經明確地將欲調用的數名稱告訴了編譯器,所以稱之為靜態連接。
動態連接與靜態連接恰巧相反,編譯器在編譯時無法得知真正要被調用的將是哪一個函數,必須根據程序在執行時所發出的信息來通知翻譯器哪一個函數將真正被調用。
在main()函數中,編譯器在編著時無法知所要調用的是哪一個函效,必須在執行程序時由用戶輸入選擇項,編譯器依據此選項來調用對應的函數。
9.2虛函數的概念
使用關鍵字Virtual說明的成員函數稱為虛函數。虛函數是在繼承的環境下最好的動態連接方式。
在繼承關係下,如果基類和派生類中定義了同名的成員函數,則當調用此函數時,我們希望編譯器能根據該調用是通過哪一個類而找到相對應的成員函數。普通函數不能做到這一點,隻有另想辦法,那就是使用虛函數。
要把類中某一成員函數定義為虛函數,隻要在成員函數原型的前麵加上關鍵字Virtu-al,那麼該成員函數就被說明為虛函數。
在類Parent中,我們說明成員函數Show()時,使用了關鍵字virtual,所以該成員函數Show()是虛函數。
當一個類中的某個成員函數被定義為虛函數後,此後該類的派生類中若有定義完全相同的成員函數時,編譯器會自動視其為虛函數,無論該成員函數原型的前商是否以關鍵字Virtual指明。
在類Parent的派生類CMW中,對成員函數Show()軍新進行了定義,盡管函數Show0前麵沒有使用關鍵字virtual,但它仍然是一個虛函數。
定義虛函數時,有以下需要注意的事項:
1.隻有類的成員函數才可以說明成虛函數。
2.靜態成員函數本質上不是類的成員,友員函數不是類的成員藤數,所以它們都不能說明成虛函數。
3.如果虛成員函數是在類的說明體之外定義的,那麼,隻須在該成員函數的函數原型前使用關鍵字virtual,在它的規定名處不用關鍵字virtual。
4.當基類的某個成員函數是虛函數時,如果派生類中定義了完全相同的函數(函數名、參數的類型和個數、返回類型完全相同),無論其函數原型中是否使用了關鍵字virtu-al,編譯器都會把派生類的該成員函數看成虛函數。換句話說,虛函數的“虛”屬性。
9.3調用虛函數
一、以靜態連接方式調用虛函數
虛函數的定義很簡單,需要注意的是如何使用這些虛函數。虛函數是在繼承的環境下最好的動態連接方式,但並非調用虛函數就代表動態連接。當以靜態連接方式調用虛函數時,便失去了定義虛函數的意義。
二、以動態連接方式調用虛函數
為了實現動態連接效果,一般使用基類指針或基類引用來訪問虛函數。
當我們通過基類指針或基類引用來訪問虛函數時,Visual C++將根據基類指針實際指向的對象的類型,或基類引用實際引用的對象的類型,來決定是調用基類中的虛函數還是派生類中的虛函數。
由此,我們得到下列結論:
1.當通過基類指針訪問虛函數時,如果基類指針實際上指向了一個派生類對象,那麼,它將調用派生類的虛成員函數。
2.當通過基類引用訪問虛函數時,如果基類引用實際上引用了一個派生類對象,那麼它將調用派生類的虛成員函數。
3.總之,當通過基類指針或引用訪問虛函數時,VisualC++將根據基類指針實際上指向的對象類型,或基類引用實際引用的對象,來決定調用哪一個虛函數。
讓我們再來看一個例子。
它接受一個基類指針作為參數,這樣,當main0函數中調用函數show()時,編譯器將根據此參數真正指向的對象的類型,來決定調用哪個類中的虛函數。
現在,我們應該明白調用虛函數過程與動態連接之間的關係。這就是所謂的“多態性。