一.重載函數
現實生活中的一個詞可能有多種含義,比如,洗衣服、洗頭、洗車,都有一個洗字,但是他們的操作方式是不一樣的。函數也一樣,有時候它們的操作不完全一樣但是名字一樣,這就是重載函數。
重載函數就是,兩個以上的函數取相同的函數名,但是函數形參的個數或者類型不同,編譯器會根據實參與形參的類型和個數進行最佳匹配,自動確定調用哪一個函數。
爲什麼要有重載函數呢?因爲如果沒有重載函數,那麼對不同類型的數據進行類似的操作也要定義不同名稱的函數,比如加法函數,就必須對整數加法和浮點數加法分別定義不同的函數名:
int nadd(int a, int b);
float fadd(float a, float b);
這樣調用需要記住的函數名太多,而且功能類似,很不方便。
形參類型不同的例子:
int add(int x,int y);
float add(float x,float y);
形參個數不同的例子:
int add(int x,int y);
int add(int x,int y,int z);
1.重載函數的形參不管是類型還是個數必須有一樣是不同的。因爲編譯器就是看實參和哪個函數的形參的類型及個數匹配,來判斷調用哪個函數,如果函數名、形參類型和個數相同,即使函數返回值類型不同,編譯器也會認爲是函數重複定義的語法錯誤,就是說它認爲是一個函數。以下兩種是錯誤的重載函數:
int add(int x,int y);
int add(int a,int b);
上面兩個函數雖然形參名不同,但是編譯器不會以形參名來區分函數,它會認爲這是一個函數。
int add(int x,int y);
void add(int x,int y);
上面這兩個函數返回值不同,確實是兩個函數,但是編譯器也不會以返回值來區分函數,也會認爲是重複定義。
2.重載函數都是進行類似的操作,不要把不同的功能定義成重載函數,否則會讓人對調用有誤解,比如:
int add(int x,int y)
{
return x+y;
}
float add(float x,float y)
{
return x-y;
}
這兩個函數一個是實現的兩個數的加法,一個是實現減法,在語法上並沒有問題。雖然功能不一樣但是都叫add,我們調用的時候是不是會混淆?
#include<iostream>
using namespace std;
int add(int m,int n); // 函數調用前必須先聲明函數原型
float add(float x,float y);
int main()
{
int m, n;
float x, y;
cout<<"Enter two integer: ";
cin>>m>>n;
cout<<"integer <<m<<'+'<<n<<"="<<add(m,n)<<endl;
cout<<"Enter two float: ";
cin>>x>>y;
cout<<"float "<<x<<'+'<<y<<"= "<<add(x,y)<<endl;
return 0;
}
int add(int m,int n)
{
return m+n;
}
float add(float x,float y)
{
return x+y;
}
屏幕先輸出Enter two integer:,我們輸入2 3,則屏幕會接着顯示integer 2+3=5,然後繼續提示Enter two float:,我們繼續輸入2.1 3.4,最後屏幕會出現float 2.1+3.4=5.5。
二.函數模板
有時候我們使用重載函數還不能達到最優的效果,比如,兩個求絕對值的函數:
int abs(int x)
{
return x<0 ? -x:x;
}
double abs(double
x)
{
return x<0 ? -x:x;
}
大家觀察下這兩個函數,這兩個函數只是返回值類型和參數類型不同,功能完全一樣,如果能有辦法寫一段通用的代碼適用於多種不同的數據類型,就是不用像上面那樣寫兩個函數而只是一段代碼就能實現兩個函數的功能,那代碼的複用性不是更高了嗎?開發效率也會提高的。這就要函數模板來實現了。
模板是由可以使用和操作任何數據類型的通用代碼構成,這種程序設計叫做參數化程序設計,因爲它把數據類型當成了參數,可以用來創建一個通用功能的函數,支持多種不同類型的形參和返回值。函數模板的定義形式是:
template <typename 標識符>
函數定義
求絕對值的函數模板示例:
#include<iostream>
using namespace std;
template <typename T>
T abs(T x)
{
return x<0 ? -x:x;
}
int main()
{
int n = 5;
double d = -2.3;
cout << abs(n) << endl;
cout << abs(d) << endl;
return 0;
}
下面分析下這個程序,編譯器會根據調用abs函數時傳入實參的類型來確定函數模板的類型參數是什麼類型。上面使用調用表達式abs(n)時,因爲n是int類型,所以模板中類型參數T就是int,然後編譯器會以函數模板爲樣板生成一個函數:
int abs(int x)
{
return x<0 ? -x:x;
}
同樣,對於調用表達式abs(d),因爲d是double類型,所以類型參數T就是double,編譯器會根據函數模板生成下面的函數:
double abs(double x)
{
return x<0 ? -x:x;
}
所以,abs(n)實際上調用的是函數模板生成的函數:int abs(int x)。而abs(d)調用的是由函數模板生成的函數:double abs(double x)。