1. 使用函數重載
【缺陷】
只要有新類型出現,就要重新添加對應函數
除類型外,所有函數的函數體都相同,代碼的複用率不高
如果函數只是返回值類型不同,函數重載不能解決
一個方法有問題,所有的方法都有問題,不好維護
2. 使用公共基類
【缺陷】
-藉助公共基類來編寫通用代碼,將失去類型檢查的優點
-對於以後實現的許多類,都必須繼承自某個特定的基類,代碼維護更加困難
3. 使用宏函數
【缺陷】
不是函數,不進行類型檢測,安全性不高,表達式複雜容易出錯
那還有什麼其他方式嗎? 能否將寫代碼的規則告訴編譯器,讓編譯器來實現呢?、
4. 泛型編程
編寫與類型無關的邏輯代碼,是代碼複用的一種手段。模板是泛型編程的基礎
函數模板
1. 概念 函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本2. 函數模板格式
template </typename t1, typename t2,......,class tn>
返回值類型 函數名(參數列表(參數如果有多個,每一個前都要加 typename 或 class, 用逗號隔開)){}
template<typename T>
T Add(const T& left, const T& right)
{
return left + right;
}
注意:typename是用來定義模板參數關鍵字,也可以使用class(切記:不能使用struct代替typename)建議儘量使用typename
函數模板還可以被定義爲inline類型的:
template<class T>
inline T Add(const T& left, const T& right)
{
return left + right;
}
注意:inline關鍵字必須放在模板形參表之後,返回值之前,不能放在template之前。
3. 函數模板實例化&參數推演
模板是一個藍圖,它本身不是類或者函數,編譯器用模板產生指定的類或者函數的特定類型版本,產生模板特定類型的過程稱爲函數模板實例化
#include<iostream>
using namespace std;
template<typename T>//相當於聲明瞭 T 這樣一個類型
inline T Add(const T& left, const T& right)
{
cout << typeid(left).name() << endl;
return left + right;
}
int main()
{
//模板函數的隱式實例化(需要根據參數的類型去推演 T 的類型)
Add(1, 2);
Add(1.0, 2.0);
Add('1', '2');
//Add(1, '1');//報錯: 模板 T 類型不明確
//模板函數的顯式實例化,已經明確指定了 T 的類型
Add<int>(1, '1');//相當於明確指定了模板裏面的 T 爲 int,會把字符類型'1'轉換爲 int 類型的 1
return 0;
}
輸出結果:
真正意義上的 Add 函數的原型
int ---> Add<int>
double--->Add<double>
char--->Add<char>
注意:模板函數的編譯分兩個階段
實例化之前,檢查模板代碼本身,是否出現簡單語法錯誤,如:模板參數列表少了 typename
在實例化後,檢查模板生成的代碼,是否所有的調用都有效,如:實例化類型不支持某些函數調用
從函數實參確定模板形參類型和值的過程稱爲模板實參推演,多個類型形參的實參必須完全匹配