PB面向對象編程研究(三)

4. 多態性

重載不是面向對象的專有技術。比如,全局的函數也有重載的概念。但是相比之下,重載在類以及類的繼承中的應用要比全局函數的重載更加頻繁和重要。

4.1. 重載

重載是實現多態的基礎,多態由重載實現,但重載並不一定實現的都是多態。重載有兩種,一種是子類對父類同名同參數函數的重載,即實現多態;第二種是,在同類或者子類中同名不同參數的函數重載,姑且稱爲類中的普通重載。

4.1.1. 重載實現多態

第一步,我們在uo_test類的基礎上,繼承uo_test類創建uo_test_inherited類,如果按照前面的步驟,該類應該已經存在;

第二步,我們打開uo_test_inherited類,選擇Function中的func函數,如下面的圖中紅色標記;

 

第三步,添加如上圖的代碼。

至此,我們完成了對父類uo_testfunc方法在uo_test_inherited中的重載,下面將實現在程序中的調用,以演示func在類中的多態性。

在演示之前,爲了明確究竟是調用的那一個類的func方法,我們將先前在uo_test中定義的func中的代碼更改爲下圖內容:

 

下面開始正式演示PB中函數的多態性。

第一步,建立一個名爲w_main_test的窗口,在窗口中添加函數polymorphism函數,如下圖:

 

第二步,在w_main_testopen事件中添加如下代碼,其中iu_testiu_test_inherited分別是uo_testuo_test_inherited的實例變量。如圖:

 

創建對象後不要忘記銷燬對象,本例中在w_main_testclose事件中進行的銷燬,這裏不作介紹。

第三步,運行程序,觀察發生的情況:

 

 

我們發現,連續彈出兩次“I am func of uo_test_inherited”的MessageBox窗口。

從運行結果我們可以知道,雖然定義的變量iu_test的類型是uo_test,但是創建的對象實體是uo_test_inherited類型,根據多態性,func應該調用的是uo_test_inherited類型中的func方法;在polymorphism函數中,雖然參數類型定義的是uo_test的類型,但是傳入的參數實際上是uo_test_inherited類型,所以調用的依然是uo_test_inheritedfunc函數。

從上面的步驟中我們發現,我們僅僅是在uo_test_inherited類中重載了uo_test類中的func(integer ai_rtn)方法,我們並沒有額外的做什麼工作,不同於C++中,我們需要將成員函數聲明爲virtual類型。所以,在PB中每一個函數都可以在類的繼承中重載實現多態,僅僅是重載。

4.1.2. 類中的普通重載

重載不僅僅是同名函數相同參數的實現多態方式的重載,也可以是同名函數不同參數的普通重載。也許,一直強調的“同名函數”似乎是多餘的,因爲,當函數名不同時也就無所謂重載了,不過,爲了清晰強調一下也是必要的。

普通重載比實現多態的重載更加直接和簡單。如下圖,我們在uo_test_inherited類中增加一個func()的無參數版本。

 

同樣,在w_main_test窗口的open事件中調用,看看實際結果如何,如下圖:

 

我們發現,雖然iu_test的創建的實際類型是uo_test_inherited,但是,在調用func()時編譯器卻提示參數個數不對,這個進一步證明了面向對象中的多態性,無論創建或者傳入的參數是什麼類型,對象的類型依然依據聲明時的類型,但是傳入的類型可以影響被重載函數(在C++中被稱爲虛擬函數)的調用。

我們去掉錯誤的代碼,運行,然後觀察結果:

 

func()無參數版被正確調用了。函數名被成功的重載了,利用不同的參數,我們可以實現不同的處理了。

4.2. 動態調用

動態調用是PB區別於其他面嚮對象語言的一個特性,例如C++。動態調用與多態類似,或者我們可以稱其爲增強的多態性。動態調用究竟是什麼,下面便研究便揭開謎底。

4.1.2節中,使用iu_test調用func()無參數版本時發生了編譯錯誤,因爲uo_test類型中沒有func()的無參數版本,但是事實上,iu_test的實例是通過uo_test_inherited類而創建的,在這個類中確實存在這個func()的無參數版本。如果實現多態性必須在每一個類中都要聲明一個func()函數,而有時在某些父類中該函數不一定必須存在,而又需要該父類作爲一個載體調用該函數該如何解決呢?面對這個問題,我們針對動態調用做個試驗。

第一步,在w_main_test窗口中將iu_test.func()的代碼更改爲iu_test.dynamic func(),保存,看看編譯器的反應,如下圖:

 

編譯器接受了,雖然uo_test類中不存在這個函數。

第二步,運行程序,看看實際結果如何:

 

 

連續出現了兩個func()無參數版本的Messagebox窗口,這說明,通過Dynamic關鍵字實現了不需要每一個類都定義同名、同參數的函數來實現函數的多態性!這是PB對多態性的一個增強。

但是我更願意說,這是對PB編譯器的一個惡意欺騙。我們可以想象一下,如果寫出這樣的代碼會怎樣:iu_test.dynamic abc()?答案是什麼,試試就知道!

PB中,所有的對象均繼承於powerobject對象,那麼,我們下面修改一下w_main_test窗口的polymorphism函數的參數試一下,看看有什麼結果。修改後的函數如下圖:

 

此時,au_test已經是PowerObject對象實例了,本身已經不具備func(integer ai_rtn)有參數版本的函數了,但是編譯器依然接受這樣的代碼,因爲我們使用了動態調用關鍵字Dynamic關鍵字。

運行程序,我們發現與先前的測試沒什麼兩樣,依然是連續兩次彈出如下圖的MessageBox窗口:

 

 

說明我們的修改並沒有對程序的運行結果造成什麼影響。

動態調用給我們提供了更靈活的工具,我們可以在我們自己編寫的類中加入同樣的函數,而這個函數對於不同的類卻有不同的操作,使用動態調用的功能,實現更普遍的代碼。

有利也有弊,由於使用Dynamic關鍵字,使編譯器不再檢查該調用的有效性,或者說,編譯器根本無法檢查該調用在運行時是否正確,就像上面的例子中調用abc()函數一樣。造成的結果便是系統的非法中斷,而拋出系統異常。

 

 

5. 總結

本文的知識點不是甚多,總結如下:

1、 屬性,類中的數據,類的特性,實現數據的封裝與隱藏

2、 方法,類的成員函數,類的行爲,實現對屬性的操作

3、 屬性與方法都具有訪問權限

4、 對於方法的參數,選擇合適的傳遞類型

5、 在構造事件中實現屬性的初始化和資源的申請

6、 在析構事件中實現資源的釋放,如:destroy iu_test

7、 繼承類的構造與析構順序都是先父後子

8、 類的繼承性與類的多態性相互關聯,繼承性是多態性的基礎

9、 父類的每一個函數都可以被子類重載,在調用時均可以實現多態調用

10、 沒有在父類定義的函數,可以使用Dynamic關鍵字實現多態調用

11、 動態調用要求程序員必須清楚運行時的每一種可能

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章