1. 程式人生 > >C++多態有哪幾種方式?

C++多態有哪幾種方式?

cti 早綁定 時間 對象 區別 父類 不同的 版本 內幕

C++多態方式:

(1)靜態多態(重載,模板)

是在編譯的時候,就確定調用函數的類型。

(2)動態多態(覆蓋,虛函數實現)

運行的時候,才確定調用的是哪個函數,動態綁定。運行基類指針指向派生類的對象,並調用派生類的函數。

虛函數實現原理:虛函數表和虛函數指針。

純虛函數: virtual int fun() = 0;

多態基礎介紹:

===============================================================================================

首先,什麽是多態(Polymorphisn)?按字面的意思就是"多種形狀"。我手頭的書上沒有找到一個多態的理論性的概念的描述。

暫且引用一下 Charlie Calverts的對多態的描述吧——多態性是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作(摘自"Delphi4 編程技術內幕")。

簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。多態性在Object Pascal和C++中都是通過虛函數(Virtual Function) 實現的。

好,接著是"虛函數"(或者是"虛方法")。虛函數就是允許被其子類重新定義的成員函數。而子類重新定義父類虛函數的做法,稱為"覆蓋"(override),或者稱為"重寫"。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

這裏有一個初學者經常混淆的概念。覆蓋(override)和重載(overload)。

上面說了,覆蓋是指子類重新定義父類的虛函數的做法。而重載,是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。其實,重載的概念並不屬於"面向對象編程",重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那麽編譯器做過修飾後的函數名稱可能是這樣的:int_func、str_func。對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的(記住:是靜態)。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!

真正和多態相關的是 "覆蓋"。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態(記住:是動態!)的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。
因此,這樣的函數地址是在運行期綁定的(晚邦定)。結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關!

那麽,多態的作用是什麽呢?我們知道,封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。

而多態則是為了實現另一個目的——接口重用!而且現實往往是,要有效重用代碼很難,而真正最具有價值的重用是接口重用,因為"接口是公司最有價值的資源。設計接口比用一堆類來實現這個接口更費時間。而且接口需要耗費更昂貴的人力的時間。"

其實,繼承的為重用代碼而存在的理由已經越來越薄弱,因為"組合"可以很好的取代繼承的擴展現有代碼的功能,而且"組合"的表現更好(至少可以防止"類爆炸")。因此筆者個人認為,繼承的存在很大程度上是作為"多態"的基礎而非擴展現有代碼的方式了.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

覆蓋是指派生類中存在重新定義的函數,其函數名、參數列、返回值類型必須同父類中的相對應被覆蓋的函數嚴格一致,覆蓋函數和被覆蓋函數只有函數體(花括號中的部分)不同.

當派生類對象調用子類中該同名函數時會自動調用子類中的覆蓋版本,而不是父類中的被覆蓋函數版本,這種機制就叫做覆蓋。

下面我們從成員函數的角度來講述重載和覆蓋的區別。

成員函數被重載的特征有:

1) 相同的範圍(在同一個類中);

2) 函數名字相同;

3) 參數不同;

4) virtual關鍵字可有可無。

覆蓋的特征有:

1) 不同的範圍(分別位於派生類與基類);

2) 函數名字相同;

3) 參數相同;

4) 基類函數必須有virtual關鍵字。

隱藏是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

1) 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(註意別與重載混淆)。

2) 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(註意別與覆蓋混淆)。

C++多態有哪幾種方式?