函數的重載
C++允許用同一函數名定義多個函數,這些函數的參數個數和參數類型不同。這就是函數的重載(function overloading)。
int max1(int a,int b, int c); double max2(double a,double b,double c); long max3(long a,long b,long c);
#include <iostream> using namespace std; int main( ) { int max(int a,int b,int c); //函數聲明 int max(int a,int b); //函數聲明 int a=8,b=-12,c=27; cout<<″max(a,b,c)=″<<max(a,b,c)<<endl; cout<<″max(a,b)=″<<max(a,b)<<endl; } int max(int a,int b,int c) //求3個整數中的最大者 { if(b>a) a=b; if(c>a) a=c; return a; } int max(int a,int b) //求兩個整數中的最大者 { if(a>b) return a; else return b; }
參數的個數和類型可以都不同。但不能只有函數的類型不同而參數的個數和類型相同。
也就是說重載與否是由參數決定的,而不是返回值決定!!!
這里引入一個概念:
函數簽名:函數的名稱及其參數類型組合在一起,就定義了一個唯一的特性,稱為函數簽名。(不包括返回類型)
c++要求重載函數具有不同的簽名。返回類型不是函數簽名的一部分。
C++ requires that overloaded functions have distinct signature.The return type is not part of a function’s signature.
例如:
int main() { int f(int); long f(int); // error C2556: “long f(int)”: 重載函數與“int f(int)”只是在返回類型上不同 void f(int); // error C2556: “void f(int)”: 重載函數與“int f(int)”只是在返回類型上不同 return 0; }
下面介紹重載中的二義性問題:
如果兩個不同寬度的數據類型進行運算時,編譯器會盡可能地在不丟失數據的情況下將它們類型統一。若float和double運算時,如果不顯式地指定為float型,會自動轉換成double型進行計算。一個整數類型int和一個浮點類型float運算時,如果不顯式地指定為int型,C++會先將整數轉換成浮點數。
int main() { float x = 2.1f; float y = 2.1; //warning C4305: “初始化”: 從“double”到“float”截斷 return 0; }
上述 語句中float y=2.1 我們以為它是一個float類型,但編譯器卻把它認為是double(因為小數默認是double型),所以給出了警示信息,一般要定義float類型,則應該改成2.1f。
通常編譯器會按照返回類型、參數類型、參數數量區 區別調用哪個重載函數,但有時候,數據類型自動轉換機制會使編譯器進入死胡同。
float fun(float a); double fun(double a); int main() { float x; x = fun(5.1); x = fun(5); // error C2668: “fun”: 對重載函數的調用不明確 return 0; }
在這里編譯器不知道應該講x=fun(5)轉換成float還是double。
有默認參數的函數
一般情況下,在函數調用時形參從實參那里取得值,因此實參的個數應與形參相同。C++允許在定義函數時給其中的某個或某些形式參數指定默認值,這樣,當發生函數調用時,如果省略了對應位置上的實參的值時,則在執行被調函數時,以該形參的默認值進行運算。有時多次調用同一函數時用同樣的實參,給形參一個默認值,這樣形參就不必一定要從實參取值了。如有一函數聲明
float area(float r=6.5); area( ); //相當於area(6.5);
如果不想使形參取此默認值,則通過實參另行給出
area(7.5); //形參得到的值為7.5,而不是6.5
實參與形參的結合是從左至右順序進行的。因此指定默認值的參數必須放在形參表列中的最右端,否則出錯。例如:
void fun1(float a,int b=0,int c,char d=’a’); //不正確 void fun2(float a,int c,int b=0, char d=’a’); //正確
在使用帶有默認參數的函數時有兩點要注意:
(1)如果函數的定義在函數調用之前,則應在函數定義中給出默認值。如果函數的定義在函數調用之后,則在函數調用之前需要有函數聲明,此時必須在函數聲明中給出默認值,在函數定義時可以不給出默認值。
(2)一個函數不能既作為重載函數,又作為有默認參數的函數。因為當調用函數時如果少寫一個參數,系統無法判定是利用重載函數還是利用默認參數的函數,出現二義性,系統無法執行。
void fun(int); //重載函數之一 void fun(int,int = 2); //重載函數之二,帶有默認參數 void fun(int = 1,int = 2); //重載函數之三,帶有默認參數 fun(3); //error: 到底調用3個重載函數中的哪個? fun(4,5) //error:到底調用后面2個重載函數的哪個?
默認參數一般在函數聲明中提供。如果程序中既有函數的聲明又有函數的定義時,則定義函數時不允許再定義參數的默認值。
void fun(int x = 0,int y = 0); void main() {} void fun(int x = 0, int y = 0) { } // error C2572: “fun”: 重定義默認參數 : 參數 2 // error C2572: “fun”: 重定義默認參數 : 參數 1
默認值可以是全局變量、全局常量,甚至是一個函數。例如:
int a=1; int fun(int); int g(int x;fun(a)); //OK,允許默認值為函數
默認值不可以是局部變量,因為默認參數的函數調用是在編譯時確定的,而局部變量的位置與值在編譯時均無法確定。
例如:
int main() { int i; void g(int x=i); // error C2587: “i”: 非法將局部變量作為默認參數 return 0; }