在面向對象中,我們可以使用重載來實現多態。
但是問題在于,重載的函數僅僅是類型不同,代碼復用率比較低,只要有新的類型出現時,就要增加對應的函數;另一方面它的代碼可維護性比較低,一個出錯可能所有的重載均出錯。
那么在C++中,我們可以使用模板來盡量避免這些問題。
一、函數模板
函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本。
函數模板格式如下:
template<typename T1, typename T2,......,typename Tn>
返回值類型 函數名(參數列表){}
其中,typename是用來定義模板參數關鍵字,也可以使用class。
要注意模板和函數之間是不需要任何符號的,并且它們可以在同一行。
下面的代碼就是使用函數模板的一個實例:
template <typename T>
void Swap(T& left,T& right)
{T temp = left;left = right;right = temp;
}
函數模板本身不是一個函數,而是“藍圖”,是編譯器用使用方式產生特定具體類型函數的模具。
模板就是將本來應該我們做的重復的事情交給了編譯器。
當使用了模板時,在編譯器編譯階段,編譯器需要根據傳入的實參類型來推演生成對應的函數,然后供調用。
假如我們用double類型使用函數模板時,編譯器通過對實參類型的推演,確定T為double,然后生成專門處理double類型參數的函數:
可以看到,類型仍然為double,變量的值也被成功交換。
實例化函數模板的方式有兩種,一種是隱式實例化,另一種是顯式實例化。
隱式實例化,即讓編譯器根據實參推演推演模板參數的實際類型,例如:
從監視1窗口可以看到,得到了正確的結果。
那么,根據剛才的Add函數模板及變量,能否這樣隱式實例化呢?
Add(num0,num2);
根據編譯器警告信息,可以明確是不可行的:
要處理這種狀況,方法有兩種,一種是用戶自己強制類型轉換:
Add((double)num0, num2);
Add(num0, (int)num2);
還有一種方法就是,顯式實例化。
只需要在函數名后的<>中指定模板參數的實際類型即可顯式實例化:
如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯。
模板參數的匹配規則如下:
1. 一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數;
2. 對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板;
3.模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換。
例如,func同時是函數,也是模板。現在在main函數中用不同的類型對其實例化,根據打印輸出結果可知,當參數為int時,調用的是函數,當參數不為int時,調用的是根據模板生成的函數:
?二、類模板
類模板是編譯器根據被實例化的類型生成具體類的模具。
在STL中,如vector,隨處可見類模板的身影:
類模板的定義格式如下:
template <class T1, class T2, ..., class Tn>
class 類模板名
{
// 類內成員定義
};
與函數模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的類型放在<>
中即可。
類模板名字不是真正的類,實例化的結果才是真正的類。
一個類模板實例化的具體例子:
??