胡傳福
(東莞理工學(xué)院 計算機學(xué)院,廣東東莞 523808)
面向?qū)ο蟪绦蛟O(shè)計的強大之處不僅在于繼承,更在于將派生類對象當(dāng)成基類對象一樣使用的能力。支持這種能力的機制主要有兩點——多態(tài)和動態(tài)綁定。多態(tài)是指同樣的消息被不同類型的對象收到以后會導(dǎo)致完全不同的行為,這里所說的消息是指對類的成員函數(shù)的調(diào)用,不同的行為是指不同的函數(shù)實現(xiàn),本質(zhì)上其實是調(diào)用了完全不同的函數(shù)[1]。
多態(tài) (polymorphism)一詞最早來源于拉丁語poly(意為多)和morphos(意為形態(tài)),意指具有多種形式或形態(tài)。它反映了人們在思索解決問題的辦法時,對相似的問題的一種求解方法,用在C++中一個最大的特點是它在軟件開發(fā)的過程中具有了“更大的靈活性”和“更多的可重用性”,它可以使得一個對象在不同的情況下具有完全不同的表現(xiàn)形態(tài)。多態(tài)的思想允許一個對象在不同的環(huán)境中具有不同的解釋,進而發(fā)揮不同的功能,具有靈活的表述能力,既支持數(shù)據(jù)抽象,又方便了軟件設(shè)計。
在面向?qū)ο蟪绦蛟O(shè)計中,多態(tài)分為四種情況:強制多態(tài)、重載多態(tài)、參數(shù)多態(tài)以及包含多態(tài)。前面兩種是專用多態(tài),后面兩種是通用多態(tài)[1]。函數(shù)重載屬于重載多態(tài);強制多態(tài)指的是將一個變元的類型加以改變,以迎合某個函數(shù)或是某個操作的要求;包含多態(tài)是指在一類類 (也就是很多個具有相似或相關(guān)功能的類,也稱類族)中定義的不同類中的同名成員函數(shù)的一種多態(tài)行為,它主要是通過虛函數(shù)來實現(xiàn)的;參數(shù)多態(tài)與類模版有關(guān),在具體使用的時侯必須要賦給一個實際的類型才能被實例化。
從實現(xiàn)的角度看,多態(tài)可以分為兩類:編譯時的多態(tài)和運行時的多態(tài)。前者是在編譯的時候確定同名操作的具體操作對象是哪個,而后者則需要在程序運行的過程中才能動態(tài)地確定操作的對象是誰。這種確定操作的具體對象的過程叫綁定或聯(lián)編。綁定是一個個計算機的程序塊進行彼此關(guān)聯(lián)的過程,例如把一個標(biāo)識符和某個對象的方法結(jié)合在一起。按照綁定的進行階段不同,綁定又分為靜態(tài)綁定和動態(tài)綁定兩種不同的方法。這兩種方法則分別對應(yīng)著多態(tài)的兩種實現(xiàn)方式。
綁定發(fā)生在編譯連接階段的情況稱靜態(tài)綁定。在編譯、連接的過程中就能根據(jù)類型匹配等相關(guān)特征確定出程序中某操作調(diào)用與執(zhí)行該操作代碼的關(guān)系,也即能夠確定某一同名標(biāo)識是要調(diào)用哪一段程序代碼。重載、強制及參數(shù)多態(tài)都是靜態(tài)綁定的。
綁定發(fā)生在程序運行階段的情況稱動態(tài)綁定。當(dāng)在編譯、連接的過程中無法解決某個綁定問題,需要等到程序運行之后準(zhǔn)確地說是程序運行的過程中才能確定,包含多態(tài)的操作對象的確定就是這種通過動態(tài)綁定來完成的。
多態(tài)是有下列4種類型和用法。
1)重載多態(tài) 又稱函數(shù)重載。當(dāng)兩個以上具有相同名稱的函數(shù),形參的類型或者個數(shù)不同時,編譯器會根據(jù)值參和形式參數(shù)的類型、個數(shù)的最佳匹配,來自動確定調(diào)用哪一個函數(shù)[1]。其中,形參的類型和個數(shù)共同構(gòu)成函數(shù)的簽名,簽名不同,即為重載函數(shù)。
如:
根據(jù)函數(shù)的簽名不同,addDigit(123,321)、addDigit(1.2345f,3.4678f)和 addDigit(12,22,32)分別被編譯成對 addDigit(int,int)、addDigit(float,float)和addDigit(int,int,int)的調(diào)用。原理在于編譯器會根據(jù)不同的參數(shù)列表對同名函數(shù)進行名字重整,然后這些同名函數(shù)就會變成不同的函數(shù)。比如說某個編譯器會它們的名字分別重整為addDigit_int()、addDigit_float()和addDigit_tri_int()。
2)強制多態(tài) 是指將一個變元的類型加以變化,以滿足某個函數(shù)或者操作的要求,上例中,加法運算符在浮點數(shù)與整型數(shù)相加時,會先進行類型強制轉(zhuǎn)換,把整型數(shù)變?yōu)楦↑c數(shù),再相加。
3)包含多態(tài) 是指在程序運行過程中動態(tài)地確定操作的具體對象。其技術(shù)基礎(chǔ)是抽象類和虛函數(shù)。例如,下面定義了一個抽象基類Machine和兩個派生于Machine的具體類Lathe和Elevator:
通過指向基類Machine的指針來操縱具體對象。通過指向基類對象的指針調(diào)用虛函數(shù),實際上調(diào)用的是被指向的那個具體對象的類的相應(yīng)成員函數(shù)。
//通過指針working任何Machine
本例中,關(guān)鍵在于多態(tài)的接口元素,也就是虛函數(shù)working()。由于working_Machine()的參數(shù)是指向基類Machine的指針,因此在編譯的時侯無法確定調(diào)用的是哪一個類的成員函數(shù)working()。而在運行的時侯,為了指派函數(shù)調(diào)用,需要訪問虛函數(shù)被調(diào)用的那個具體對象的完整的數(shù)據(jù)類型。如此一來,用Lathe對象調(diào)用working_Machine(),實際上調(diào)用的是Lathe::working(),而用Elevator對象卻調(diào)用的是Elevator::working()。
4)參數(shù)多態(tài) 參數(shù)多態(tài)與模版類相關(guān)聯(lián),在使用的時侯必須賦給具體的類型才能被實例化,這種由類模版實例化的所有的類都具有相同的操作,但操作對象的類型卻是各不相同的。下面用參數(shù)多態(tài)重寫3)中的例子,不再定義Machine類層次結(jié)構(gòu),只定義兩個彼此無關(guān)的具體類Lathe和Elevator。
編譯器處理后,會得到working_Machine<Lathe>()和 working_Machine<Elevator>()兩個不同的函數(shù),這點和動態(tài)多態(tài)不同,動態(tài)多態(tài)憑借虛函數(shù)分派機制,在運行期只有一個working_Machine()函數(shù)。
在數(shù)據(jù)結(jié)構(gòu)中,數(shù)據(jù)由數(shù)據(jù)項組成;數(shù)據(jù)項是一條信息,或者是其值屬于某種數(shù)據(jù)類型的一條記錄,它是數(shù)據(jù)類型的成員;類型是一組值的集合;數(shù)據(jù)類型是指一種類型以及定義在該種類型上的一組操作;抽象數(shù)據(jù)類型 (abstract data type,簡稱ADT)一般是指數(shù)據(jù)結(jié)構(gòu)作為某個軟件組件的實現(xiàn)。ADT的接口用一種類型加上該種類型上的一組操作定義,而每個操作又由它的輸入和輸出單獨定義。ADT并不關(guān)心數(shù)據(jù)類型如何實現(xiàn),實現(xiàn)細節(jié)對于ADT的用戶來說通常是隱藏的,通過封裝來阻止外部對象對它的訪問[3]。
數(shù)據(jù)結(jié)構(gòu)是ADT的實現(xiàn)。在C++這類面向?qū)ο蟮某绦蛟O(shè)計語言中,ADT及其實現(xiàn)一起共同組成了類。同ADT有關(guān)的每個操作全部由成員函數(shù)來實現(xiàn)。ADT與具體應(yīng)用無關(guān),這樣可以讓程序員把精力更多地用在數(shù)據(jù)及其操作的理想模型上。
而所謂參數(shù)化多態(tài),本質(zhì)上就是將程序要處理的對象的數(shù)據(jù)類型參數(shù)化,使一段程序可以用來處理多種不同類型的對象。類模版能讓用戶為類聲明一種模式,使得類中的數(shù)據(jù)成員、成員函數(shù)的參數(shù)、成員函數(shù)的返回值可以取任意類型[4]。
數(shù)據(jù)結(jié)構(gòu)的ADT的特性和模版類的特點可以說是相通的。模版類對數(shù)據(jù)結(jié)構(gòu)來說是應(yīng)運而生,傳統(tǒng)的數(shù)據(jù)結(jié)構(gòu)在模版類的描述下更是煥發(fā)出了新的生機與活力。
動態(tài)多態(tài)只需要一個函數(shù),生成的可執(zhí)行代碼尺寸比較小,靜態(tài)多態(tài)必須要對不同的類型生成不同的模板實體,尺寸要大一點,但是因為不需通過指針進行間接操作,所以生成的代碼也會更快一點。靜態(tài)多態(tài)與動態(tài)多態(tài)相比,因為全部綁定發(fā)生在編譯的時候,所也更加類型安全。因為系統(tǒng)不允許將一個錯誤類型的對象插入到從一個模板類實例化而來的容器中的。此外,動態(tài)多態(tài)在處理異質(zhì)對象集合上就優(yōu)雅得多了,靜態(tài)多態(tài)卻可以實現(xiàn)安全、高效的同質(zhì)對象集合操作,并為C++帶來了泛型編程的概念。
數(shù)據(jù)結(jié)構(gòu)經(jīng)過幾十年的發(fā)展,于上個世紀(jì)成熟穩(wěn)定,許多算法早在20世紀(jì)六、七十年代已成型,不少算法思想早于計算機出現(xiàn)。多態(tài)算法思想的提出為古老的數(shù)據(jù)結(jié)構(gòu)注入了新的生機與活力,必將為計算機科學(xué)技術(shù)的發(fā)展提供新的動力。
[1]鄭莉,董淵,何江舟.C++語言程序設(shè)計[M].4版.北京:清華大學(xué)出版社,2010.
[2]Mark Allen Weiss.?dāng)?shù)據(jù)結(jié)構(gòu)與算法分析C++描述[M].3版.張懷勇,譯.北京:人民郵電出版社,2007.
[3]Clifford A.Shaffer.?dāng)?shù)據(jù)結(jié)構(gòu)與算法分析(C++版)[M].2版.張銘,劉曉丹,譯.北京:電子工業(yè)出版社,2002.
[4]Malik D S.C++編程—數(shù)據(jù)結(jié)構(gòu)與程序設(shè)計方法[M].晏海華,蔡旭輝,常鴻,等譯.北京:電子工業(yè)出版社,2003.