摘要:函數重載機制是C++語言中的關健技術之一,它允許功能相近的函數在相同的作用域內以相同函數名定義,編程中合理的使用函數重載,可以增強程序設計的靈活性、擴充性和可讀性。
關鍵詞:函數重載;編譯器;覆蓋
中圖分類號:TP311文獻標識碼:A文章編號:1009-3044(2008)28-0135-02
Application of Function Overloading in C++
LIN Yong
(Department of Computer, Shanxi University of Technology, Hanzhong 723001, China)
Abstract: Overloaded function is one of the key technology in C++ language,which allows name same definition with similar function withinn the same role.Using of overloaded function rationally in programming can enhance the flexibility, Scalability and readability of the process design.
Key words: function overload; Compiling device; Cover
1 引言
相對C語言的函數,C++增加了重載(overloaded)機制。重載機制既可用于全局函數,也可用于類的成員函數。函數的重載也稱多態函數,對于沒有重載機制的C語言,每個函數必須有其不同于其它函數的名稱,即使操作是相同的,僅僅數據的類型不同,也需要定義名稱完全不同的函數,這樣就顯得重復且效率低,而具有重載機制的C++語言,允許功能相近的函數在相同的作用域內以相同函數名定義,因而使函數方便使用,便于記憶,也使程序設計更加靈活。
基于C++相對于C語言的重載的優點,合理的使用重載,將大大地提高程序的運行效率和代碼的可讀性。
2 重載的概念
在C++程序中,可以將語義、功能相似的幾個函數用同一個名字表示,即函數重載。這樣便于記憶,提高了函數的易用性。例如下面的函數EatBeef,EatFish,EatChicken可以用同一個函數名Eat表示,用不同類型的參數加以區別。
void EatBeef(…);// 可以改為 void Eat(Beef …);
void EatFish(…);// 可以改為 void Eat(Fish …);
void EatChicken(…); // 可以改為 void Eat(Chicken …);
3 重載實現的原理
幾個同名的重載函數仍然是不同的函數,如何區分它們呢?我們自然想到函數接口的兩個要素:參數與返回值。如果同名函數的參數不同(包括類型、順序不同),那么它們是不同的函數;如果同名函數僅僅是返回值類型不同,有時可以區分,有時卻不能。例如:
void Function(void);
int Function (void);
上述兩個函數,第一個沒有返回值,第二個的返回值是int類型。如果有調用函數語句:int x = Function();則可以判斷出Function是第二個函數。但在C++/C程序中,可以忽略函數的返回值。在這種情況下,編譯器和程序員就不知道哪個Function函數被調用。所以只能靠參數而不能靠返回值類型的不同來區分重載函數。編譯器根據參數為每個重載函數產生不同的內部標識符。
但并不是兩個函數的名字相同就能構成重載。全局函數和類的成員函數同名不算重載,因為函數的作用域不同。例如:
void Print(…);// 全局函數
class A
{…
void Print(…); // 成員函數
}
不論兩個Print函數的參數是否不同,如果類的某個成員函數要調用全局函數Print,為了與成員函數Print區別,全局函數被調用時應加‘::’標志。例如:
::Print(…);// 表示Print是全局函數而非成員函數
4 函數重載的實現
4.1 非成員函數重載
非成員函數重載是C++過程性部分,形式有:
1) 重載函數使用不同類型的參數,例如:
int min(int x,int y) {return x double min(int x,int y) {return x 在調用過程中,C++編譯器將根據調用式中實參的類型決定調用哪一個重載函數,如函數調用min(2,3),編譯器將調用執行時代碼定到第一個函數體上,而函數調用min(2.5,3.5)就被被編譯器定到第二個函數體上。 2) 重載函數中使用不同數目的參數,例如: int min(int x,int y) {return x int min(int x,int y,int z) {int t=x trturn t C++編譯器將根據函數調用表達式中實參的個數,對于調用min(10,20)編譯器確定調用執行代碼是一個重載函數的函數體,對于調用min(10,20,30)編譯器確定調用執行代碼是第二個重載函數的函數體。 因此,在C++中,利用重載機制把調用重載工作交給編譯器大大提高了程序的靈活性和擴展性。 4.2 成員函數重載 C++類部分,類中的成員函數也可以被重載,有下面兩種方式: 1) 類成員函數重載 #include class List//實現一個List類 {float x,y; public: List() {x=0.0;y=0.0;} List(float x1,float y1) //重載構造函數 {x=x1;y=y1;} void set(float m,float n) {List::x=m; List::y=n;} void set(List p)//重載set成員函數 {x=p.x;y=p.y} void Length( void) {cout<<\"Length of List is\"<<0.0<<\"\";} } C++允許成員函數重載為定義類相似但不相同的行為提供了同名方便,使面向對象程序易讀同時易理解。重載構造函數可使對象方便初始化。類的對象調用哪個重載函數由C++編譯器根據調用表達式中的參數個數、類型匹配重載成員函數,例如:List a,b(1.0,2.0); 定義兩個對象,對a編譯器調用List()函數,對b編譯器調用List(1.0,2.0)函數。 2) 派生類中對基類成員函數的重載 派生類繼承了基類的所有功能,基類操作可作用于派生類對象。但派生類是基類的繼承,還增加了一些新的函數成員和數據成員,補充新的操作,如: #include class circle:public point {float r; public: circle(point p,int r1):point(p) //類circle構造函數 {r=r1;} void area() {cout<<\"area of circle of\"<<r<<\"is\"<<3.1416*r*r;}} void main() {point p1(2,10),*p2; circle c(p1,1); p2=p1; p2->area(); p2=c;p2->area();} 程序運行結果為area of point is 0.0area of point is 0.0 原因是:circie類重載了基類point的成員函數area(),要求輸出的內容不同。當主函數main()中調用area()時,C++編譯器就要決定哪一個area()函數被調用。一般情況下,類能有效地解決重載問題,p2是point的一個對象,p2.point()將調用point::area();c是circle的一個對象,c.area()將調用circle::area()。在表達式p2->area();中我們希望當p2 指向類point的一個對象時,調用point::area(),而當p2 指向類circle的一個對象時,就調用circle::area(),但c++對重載函數進行先期聯編且派生類具有基類功能。因此,C++編譯依據c++的基類決定調用基類point聲明的函數,為此程序執行后的結果數據是0.0,而不是希望的3.1416。 上述為c++繼承機制應用多態性技術利用虛函數解決基類和派生類成員函數重載問題,從而實現一個接口,多種方法,給c++面向對象的程序增加了靈活性、可擴充性。 5 隱式類型轉換導致重載函數產生二義性 先看下面的程序段: # include <iostream.h> void output(int x); // 函數聲明 void output(float x);// 函數聲明 void output(int x) { cout << \"output int\" << x << endl; } void output( float x) { cout << \"output float\" << x << endl; } void main(void) { int x = 1; float y = 1.0; output(x); // output int 1 output(y); // output float 1 output(1); // output int 1 // output(0.5); // error! ambiguous call, 因為自動類型轉換 output(int(0.5)); // output int 0 output(float(0.5)); // output float 0.5 } 上面的程序代碼中:第一個output函數的參數是int類型,第二個output函數的參數是float類型。由于數字本身沒有類型,將數字當作參數時將自動進行類型轉換(稱為隱式類型轉換)。語句output(0.5)將產生編譯錯誤,因為編譯器不知道該將0.5轉換成int還是float類型的參數。隱式類型轉換在很多地方可以簡化程序的書寫,但是也可能留下隱患。 6 重載與覆蓋的區別 覆蓋是指派生類函數覆蓋基類函數。重載與覆蓋的區別主要由它們的特征所決定。成員函數被重載的特征:相同的范圍(在同一個類中);函數名字相同;參數不同;virtual關鍵字可有可無。覆蓋的特征是:不同的范圍(分別位于派生類與基類);函數名字相同;參數相同;基類函數必須有virtual關鍵字。 7 值得注意的隱藏規則 僅僅區別重載與覆蓋并不算困難,但是C++的隱藏規則使問題復雜性陡然增加。這里“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下: 1) 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。 2) 如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。 8 結束語 綜上所述,通過從概念、定義、使用及使用規則等方面對C++函數的重載進行了探討,以便在編程時合理的使用重載,有助于提高程序的運行效率和代碼的可讀性。 參考文獻: [1] 鄭莉. C++語言程序設計[M]. 北京:清華大學出版社,2000. [2] 劉斌. 面向對象程序設計Visual C++[M]. 北京:清華大學出版社,2003. [3] 揣錦華. C++程序設計語言[M]. 西安:西安電子科技大學出版社,2003. [4] 張國峰. C++語言及其程序設計教程[M]. 北京:電子工業出版社,1997.