函式的過載

2021-04-13 00:32:32 字數 2568 閱讀 6787

在面向過程的程式設計模式下,程式被劃分為資料結構+函式,這也就是那句著名的"程式=資料結構+演算法"的由來

在面向過程的思考方式下,函式名就是最好的標識,而名稱組織良好的函式集合是判斷**是否優美的乙個重要因素

在物件導向的思考方式下,一切都不同了

常見的情況下,應該為不同的函式起不同的名字,但是當這些函式要完成相同的功能而僅僅是要處理的引數型別不同時,

根據完成的功能而給這些函式乙個相同的名字反到更符合我們的生活習慣,例如我們熟悉的print函式

void print(int); //列印乙個整數

void print(float); //列印乙個浮點數

void print(const char*); //列印乙個字串

...如果我們不得不把這些函式宣告為

void print_int(int); //列印乙個整數

void print_float(float); //列印乙個浮點數

void print_str(const char*); //列印乙個字串

...就有些笨拙了,如果不考慮系統對隱式型別轉換的支援,我們甚至需要為8種整型資料分別命名不同的函式

這可以讓程式設計師寫出更健壯的**,有人或許要開始爭辯了

不是嗎,你只能寫printint(3)而不可能寫出printint(3.2)的**,如果名稱只有乙個,那麼

print(3),這到底該呼叫哪乙個函式呢?

對,我承認這種笨拙的命名方式會帶來一些健壯性的好處,但是這是以降低程式設計師工作效率為代價的,說到底,這仍然是乙個取捨,

僅僅就這個問題,其實我們誰也所服不了誰的,那麼請看看下乙個例子

int i = 10;

float f = 10f;

double d = 10;

i + f;

i + d;

f + d;

對於運算子+來說,這裡其實就是乙個同樣函式名的例子.對於c++來說,它支援運算子的重定義,這也是讓使用者自定義型別具有內建型別同樣的能力的原則體現,

那麼這裡就不得不支援同名的函式,另外也需要說明一下,在乙個函式只有乙個名稱的情況下,如果多個引數具有不同的型別,這種規則很快會導致函式名稱的

膨脹,在c語言中,幾萬行**的乙個檔案的情況是很常見的,也有這方面的原因

好了,好了,現在的問題已經不是是否需要函式同名的問題了,現在的問題是我們如何解決函式同名的問題

畢竟對於編譯器來說,函式名稱就是它的符號表內容,如果一組函式具有相同的名稱,那麼編譯器就必須自己把它們區別開來

這個問題的解決其實非常簡單,就是名稱重整(name-mangling),編譯器根據函式的名稱和所有引數的型別為這個函式重新整理乙個函式名稱,並用這個整理後的名稱作為標識,當然

,顯示的時候我們可不想看到那一長串名稱,因此這個名稱重整還必須能夠很容易的得到原始的函式名,例如以下函式的名稱重整

轉換前void print(int); //列印乙個整數

void print(float); //列印乙個浮點數

void print(const char*); //列印乙個字串

...轉換後:

void print_int(int); //列印乙個整數

void print_float(float); //列印乙個浮點數

void print_str(const char*); //列印乙個字串

...呵呵,還不是c語言的老把戲,只不過這次是編譯器來做,而不是程式設計師做的而已

嗯,如果泛泛的說,c++不過是把以前許多c語言的實現方式固定化,並移到了編譯器層次來實現而已

另乙個更重要的問題是,程式設計師也不會寫那個編譯器自己才認識的重整名字,因此當程式設計師寫出

print(3)這樣的**以後,編譯器還必須把這裡的print呼叫編譯成它可以認識的重整名稱,而這就有些難度了

基本的想法是編譯器必須去挑選那個引數匹配最好的函式,而到底什麼才算是匹配最好呢?c++給出了一套複雜的匹配規則:

1.準確匹配:也就是說,無須任何轉換或者只需要一些微不足道的轉換(例如陣列名到指標,函式名到函式指標,t到const t等)

2.提公升匹配:也就是說,只需要型別公升級後就可以匹配的(例如bool到int,short到int,float到double等)

3.標準匹配:也就是說,利用標準支援的一些型別轉換(例如int到double,double到int,derived*到base*,t*到void*等)

4.使用者匹配:也就是說,利用使用者自定義的型別轉換

5.省略匹配:也就是說,利用...引數進行匹配

這5個規則是5個層次,依照從上向下的順序執行,如果分析到某個層次卻發現了兩個以上的匹配則被認為歧義

過載規則與函式的宣告次序和函式的返回值無關,更特別的過載規則與不同的非名字空間作用域也無關

這樣的規定將避免過載規則與具體的呼叫環境相關,我們不想讓已經夠複雜的規則更加複雜

過載不能跨作用域,這將導致艱難的權衡

如果是有意識的用同名的函式去遮蔽,這就是乙個很有用的技術,使我們的小世界可以免收外界的干擾

如果是無意識的用同名的函式去遮蔽,這就是乙個很糟糕的技術,使我們的小世界和外面的世界失去了聯絡

權衡是如此的困難,因此還必須保留一扇門,如果希望過載能夠跨越類或者名字空間的作用域,可以使用using使用宣告 

函式的過載

在程式設計時,有時我們要實現的是同一類的功能,只是有些細節不同 例如希望從3個數中找出其中的最大者,而每次求最大數時資料的型別不同,可能是3個整數 3個雙精度數或3個長整數 程式設計者往往會分別設計出3個不同名的函式,其函式原型為 int max1 int a,int b,int c 求3個整數中的...

函式的過載

函式的過載即對乙個函式名重新賦予它新的含義,使乙個函式名可以多用,即一物多用。求3個數中的最大數 includeusing namespace std int max int a,int b,int c long max long a,long b,long c double max double ...

函式的過載

在實際開發中,有時候我們需要實現幾個功能類似的函式,只是有些細節不同。例如希望交換兩個變數的值,這兩個變數有多種型別,可以是 int float char bool 等,我們需要通過引數把變數的位址傳入函式內部。在 c語言中,程式設計師往往需要分別設計出三個不同名的函式,其函式原型與下面類似 voi...