本篇博客主要講解C++模版的相關內容。?
目錄
1.泛型編程
?2.函數模板
2.1 函數模版概念
2.2 函數模版格式
2.3?函數模版的原理
?2.4 函數模版的實例化
? ?1.隱式實例化:讓編譯器根據實參推演模板參數的實際類型
2.?顯式實例化:在函數名后的<>中指定模板參數的實際類型
2.5?模板參數的匹配原則
3. 類模板
?4.小結
1.泛型編程
如何實現一個通用的交換函數?
void Swap(int& left, int& right) {int temp = left;left = right;right = temp; } void Swap(double& left, double& right) {double temp = left;left = right;right = temp; } void Swap(char& left, char& right) {char temp = left;left = right;right = temp; }
使用函數重載雖然可以實現,但是有一下幾個不好的地方:
1. 重載的函數僅僅是類型不同,代碼復用率比較低,只要有新類型出現時,就需要用戶自己增加對應的函數
2. 代碼的可維護性比較低,一個出錯可能所有的重載均出錯
那能否告訴編譯器一個模子,讓編譯器根據不同的類型利用該模子來生成代碼呢?
?
?如果在C++中,也能夠存在這樣一個模具,通過給這個模具中填充不同材料(類型),來獲得不同材料的鑄件(即生成具體類型的代碼),那將會節省許多頭發。巧的是前人早已將樹栽好,我們只 需在此乘涼。
泛型編程:編寫與類型無關的通用代碼,是代碼復用的一種手段。模板是泛型編程的基礎。
?2.函數模板
2.1 函數模版概念
?函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生 函數的特定類型版本。
2.2 函數模版格式
template<typename T1,typename T2,......,typename Tn>
template返回值類型 函數名(參數列表){}
template<typename T> void Swap( T& left, ?T& right) { T temp = left;left = right; right = temp; }
注意:typename是用來定義模板參數關鍵字,也可以使用class(切記:不能使用struct代替class)
2.3?函數模版的原理
函數模板是一個藍圖,它本身并不是函數,是編譯器用使用方式產生特定具體類型函數的模具。所以其實模板就是將本來應該我們做的重復的事情交給了編譯器
? ? 在編譯器編譯階段,對于模板函數的使用,編譯器需要根據傳入的實參類型來推演生成對應類型的函數以供調用。比如:當用double類型使用函數模板時,編譯器通過對實參類型的推演,將T確定為double類型,然后產生一份專門處理double類型的代碼,對于字符類型也是如此。
?2.4 函數模版的實例化
用不同類型的參數使用函數模板時,稱為函數模板的實例化。模板參數實例化分為:隱式實例化和顯式實例化。
? ?1.隱式實例化:讓編譯器根據實參推演模板參數的實際類型
template<class T> T Add(const T& left, const T& right) {return left + right; } int main() {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);// Add(a1, d1);Add(a, (int)d);return 0; }
詳解代碼:Add(a1, d1); 該語句不能通過編譯,因為在編譯期間,當編譯器看到該實例化時,需要推演其實參類型,通過實參a1將T推演為int,通過實參d1將T推演為double類型,但模板參數列表中只有 一個T,編譯器無法確定此處到底該將T確定為int 或者 double類型而報錯。
處理方式:1. 用戶自己來強制轉化? 2. 使用顯式實例化
2.?顯式實例化:在函數名后的<>中指定模板參數的實際類型
int main(void)
{int a = 10;double b = 20.0;// 顯式實例化Add<int>(a, b);return 0;
}
如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯。
2.5?模板參數的匹配原則
1. 一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數
?// 專門處理int的加法函數int Add(int left, int right){return left + right;}// 通用加法函數template<class T>T Add(T left, T right){return left + right;}void Test(){Add(1, 2); ? ? ? // 與非模板函數匹配,編譯器不需要特化Add<int>(1, 2); ?// 調用編譯器特化的Add版本}
2. 對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而 不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板
// 專門處理int的加法函數int Add(int left, int right){return left + right;
}// 通用加法函數template<class T1, class T2>T1 Add(T1 left, T2 right){return left + right;}void Test(){Add(1, 2); ? ? // 與非函數模板類型完全匹配,不需要函數模板實例化Add(1, 2.0); ? // 模板函數可以生成更加匹配的版本,編譯器根據實參生成更加匹配的
Add函數}
?3. 模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換
3. 類模板
?類模板實例化與函數模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的 類型放在<>中即可,類模板名字不是真正的類,而實例化的結果才是真正的類。
// Stack是類名,Stack<int>才是類型
Stack<int> st1; ? ?// int
Stack<double> st2; // double
?4.小結
以上便是本篇博客的所有內容了,如果大家學到知識的話,還請給博主點點贊!!!
?