C 多型技術 靜態多型和動態多型

2021-05-23 10:53:28 字數 4789 閱讀 7123

多型(polymorphism)一詞最初**於希臘語polumorphos,含義是具有多種形式或形態的情形。在程式設計領域,乙個廣泛認可的定義是 「一種將不同的特殊行為和單個泛化記號相關聯的能力」。和純粹的物件導向程式設計語言不同,c++中的多型有著更廣泛的含義。除了常見的通過類繼承和虛函 數機制生效於執行期的動態多型(dynamic polymorphism)外,模板也允許將不同的特殊行為和單個泛化記號相關聯,由於這種關聯處理於編譯期而非執行期,因此被稱為靜態多型 (static polymorphism)。 

事實上,帶變數的巨集和函式過載機制也允許將不同的特殊行為和單個泛化記號相關聯。然而,習慣上 我們並不將它們展現出來的行為稱為多型(或靜態多型)。今天,當我們談及多型時,如果沒有明確所指,預設就是動態多型,而靜態多型則是指基於模板的多型。 不過,在這篇以c++各種多型技術為主題的文章中,我們首先還是回顧一下c++社群爭論已久的另一種「多型」:函式多型(function polymorphism),以及更不常提的「巨集多型(macro polymorphism)」。 

函式多型

也就是我們常說的函式過載(function overloading)。基於不同的引數列表,同乙個函式名字可以指向不同的函式定義: 

// overload_poly.cpp

#include

#include

// 定義兩個過載函式

int my_add(int a, int b)

int my_add(int a, std::string b)

int main()

根 據引數列表的不同(型別、個數或兼而有之),my_add(1, 2)和my_add(1, "2")被分別編譯為對my_add(int, int)和my_add(int, std::string)的呼叫。實現原理在於編譯器根據不同的引數列表對同名函式進行名字重整,而後這些同名函式就變成了彼此不同的函式。比方說,也許 某個編譯器會將my_add()函式名字分別重整為my_add_int_int()和my_add_int_str()。 

巨集多型

帶變數的巨集可以實現一種初級形式的靜態多型: 

// macro_poly.cpp

#include

#include

// 定義泛化記號:巨集add

#define add(a, b) (a) + (b);

int main()

當程式被編譯時,表示式add(i1, i2)和add(s1, s2)分別被替換為兩個整數相加和兩個字串相加的具體表示式。整數相加體現為求和,而字串相加則體現為連線。程式的輸出結果符合直覺: 

1 + 2 = 3

hello, + world! = hello, world!

動態多型

這就是眾所周知的的多型。現代物件導向語言對這個概念的定義是一致的。其技術基礎在於繼承機制和虛函式。例如,我們可以定義乙個抽象基類vehicle和兩個派生於vehicle的具體類car和airplane: 

// dynamic_poly.h

#include

// 公共抽象基類vehicle

class vehicle

;// 派生於vehicle的具體類car

class car: public vehicle

};// 派生於vehicle的具體類airplane

class airplane: public vehicle

}; 客戶程式可以通過指向基類vehicle的指標(或引用)來操縱具體物件。通過指向基類物件的指標(或引用)來呼叫乙個虛函式,會導致對被指向的具體物件之相應成員的呼叫:

// dynamic_poly_1.cpp

#include

#include

#include "dynamic_poly.h"

// 通過指標run任何vehicle

void run_vehicle(const vehicle* vehicle)

int main()

此 例中,關鍵的多型介面元素為虛函式run()。由於run_vehicle()的引數為指向基類vehicle的指標,因而無法在編譯期決定使用哪乙個版 本的run()。在執行期,為了分派函式呼叫,虛函式被呼叫的那個物件的完整動態型別將被訪問。這樣一來,對乙個car物件呼叫 run_vehicle(),實際上將呼叫car::run(),而對於airplane物件而言將呼叫airplane::run()。

或許動態多型最吸引人之處在於處理異質物件集合的能力: 

// dynamic_poly_2.cpp

#include

#include

#include "dynamic_poly.h"

// run異質vehicles集合

void run_vehicles(const std::vector& vehicles)

}int main()

在run_vehicles()中,vehicles[i]->run()依據正被迭代的元素的型別而呼叫不同的成員函式。這從乙個側面體現了物件導向程式設計風格的優雅。

靜態多型

如果說動態多型是通過虛函式來表達共同介面的話,那麼靜態多型則是通過「彼此單獨定義但支援共同操作的具體類」來表達共同性,換句話說,必須存在必需的同名成員函式。 

我們可以採用靜態多型機制重寫上一節的例子。這一次,我們不再定義vehicles類層次結構,相反,我們編寫彼此無關的具體類car和airplane(它們都有乙個run()成員函式): 

// static_poly.h

#include

//具體類car

class car

};//具體類airplane

class airplane

};run_vehicle()應用程式被改寫如下:

// static_poly_1.cpp

#include

#include

#include "static_poly.h"

// 通過引用而run任何vehicle

template

void run_vehicle(const vehicle& vehicle)

int main()

現 在vehicle用作模板引數而非公共基類物件(事實上,這裡的vehicle只是乙個符合直覺的記號而已,此外別無它意)。經過編譯器處理後,我們最終 會得到run_vehicle()和 run_vehicle()兩個不同的函式。這和動態多型不同,動態多型憑藉虛函式分派機制在執行期只有乙個 run_vehicle()函式。 

我們無法再透明地處理異質物件集合了,因為所有型別都必須在編譯期予以決定。不過,為不同的vehicles引入不同的集合只是舉手之勞。由於無需再將集合元素侷限於指標或引用,我們現在可以從執行效能和型別安全兩方面獲得好處: 

// static_poly_2.cpp

#include

#include

#include "static_poly.h"

// run同質vehicles集合

template

void run_vehicles(const std::vector& vehicles)

}int main()

兩種多型機制的結合使用

在 一些高階c++應用中,我們可能需要結合使用動態多型和靜態多型兩種機制,以期達到物件操作的優雅、安全和高效。例如,我們既希望一致而優雅地處理 vehicles的run問題,又希望「安全而高效」地完成給飛行器(飛機、飛艇等)進行「空中加油」這樣的高難度動作。為此,我們首先將上面的 vehicles類層次結構改寫如下: 

// dscombine_poly.h

#include

#include

// 公共抽象基類vehicle

class vehicle

;// 派生於vehicle的具體類car

class car: public vehicle

};// 派生於vehicle的具體類airplane

class airplane: public vehicle

void add_oil() const

};// 派生於vehicle的具體類airship

class airship: public vehicle

void add_oil() const

};我們理想中的應用程式可以編寫如下:

// dscombine_poly.cpp

#include

#include

#include "dscombine_poly.h"

// run異質vehicles集合

void run_vehicles(const std::vector& vehicles)

}// 為某種特定的aircrafts同質物件集合進行「空中加油」

template

void add_oil_to_aircrafts_in_the_sky(const std::vector& aircrafts)

}int main()

我 們保留了類層次結構,目的是為了能夠利用run_vehicles()一致而優雅地處理異質物件集合vehicles的run問題。同時,利用函式模板 add_oil_to_aircrafts_in_the_sky(),我們仍然可以處理特定種類的vehicles — aircrafts(包括airplanes和airships)的「空中加油」問題。其中,我們避開使用指標,從而在執行效能和型別安全兩方面達到了預 期目標。

結語

C 多型 靜態多型與動態多型

多型 顧名思義,多型就是多種形態,也就是對不同物件傳送同乙個訊息,不同物件會做出不同的響應。並且多型分為靜態多型和動態多型。靜態多型就是在系統編譯期間就可以確定程式執行到這裡將要執行哪個函式,例如 函式的過載,物件名加點操作符執行成員函式等,都是靜態多型,其中,過載是在形成符號表的時候,對函式名做了...

靜態多型 動態多型

又稱編譯期多型,即在系統編譯期間就可以確定程式將要執行哪個函式。例如 函式過載,通過類成員運算子指定的運算。函式過載示例 class a a int x void f void f int x class b void f void f int x 以上,類a中兩個a 是函式過載,兩個f 是函式過載...

C 靜態多型與動態多型

多型按字面的意思就是多種形態,相同的方法呼叫,但是有不同的實現方式。多型性可以簡單地概括為 乙個介面,多種方法 c 有兩種多型形式 靜態多型 也稱為編譯期間的多型,編譯器在編譯期間完成的,編譯器根據函式實參的型別 可能會進行隱式型別轉換 可推斷出要呼叫那個函式,如果有對應的函式就呼叫該函式,否則出現...