??? ???🔥🔥 歡迎來到小林的博客!!
??????🛰?博客主頁:??林 子
??????🛰?博客專欄:?? C++
??????🛰?社區 :?? 進步學堂
??????🛰?歡迎關注:👍點贊🙌收藏??留言
目錄
- 認識模板
- function template函數模板
- class類模板
- 模板特化
- 半特化(偏特化)
- 模板原理
認識模板
模板是C++中泛型編程的基礎,一個模板就是一個創建類或者函數的藍圖或者說是公式。
當使用一個vector這樣的泛型類型時,或者find這樣的泛型函數時,我們提供足夠的信息,將藍圖轉化成類或者函數。這種轉化發生在編譯時。
function template函數模板
比如我們想寫一個swap函數來交換兩個數的值,在C++中我們可以用函數重載來重載多個函數來實現對不同類型的交換。比如我們要交換int 和 double類型,那么僅僅重載一個 swap(int& x ,int& y)是不夠的。還需要重載一個swap(double& x,double& y) 函數。 但如果我還想交換其他的類型,也要寫出其他類型對應的函數重載。這樣就顯得十分麻煩。所以這時候我們就可以function template(函數模板) 來減少我們需要寫的代碼。
template<class T> //定義函數模板
void Swap(T& x, T& y) //T是模板參數,也就是你傳入的參數
{T tmp = x;x = y; y = tmp;
}
int main()
{int i1 = 5, i2 = 10;double d1 = 5.5, d2 = 10.5;string str1 = "hello", str2 = "world";cout << "交換前:" << endl;cout << "i1 : " << i1 << " , i2 :" << i2 << endl;cout << "d1 : " << d1 << " , d2 :" << d2 << endl;cout << "str1 : " << str1 << " , str2 :" << str2 << endl;Swap(i1, i2);Swap(d1, d2);Swap(str1, str2);cout << "交換后:" << endl;cout << "i1 : " << i1 << " , i2 :" << i2 << endl;cout << "d1 : " << d1 << " , d2 :" << d2 << endl;cout << "str1 : " << str1 << " , str2 :" << str2 << endl;return 0;
}
代碼運行結果:
我們寫了一份代碼,就可以交換所有類型,而不用頻繁的去自己寫函數重載。
class類模板
類模板是用來生成類的藍圖,與函數模板不同的是,編譯器不能為類模板推斷模板參數類型。為了使用類模板,我們必須在類名后面加<>中提供額外信息。
template <class T>
class Node
{
public:void fun(){cout << "T* : " << typeid(next).name() << endl;cout << "T : " << typeid(val).name() << endl;}
private:T* next;T val;
};int main()
{Node<int> ni;ni.fun();cout << endl;Node<double> nd;nd.fun();cout << endl;Node<char> nc;nc.fun();return 0;
}
運行結果:
此時我們就可以發現,你<>中傳什么類型進去,那么你類中的T就是什么類型。這就大大的方便了我們的編碼,如果沒有模板,那么每個類型都要安插一個這樣的類出來,是非常麻煩的。
模板特化
如果你想對不同的模板參數,做不同的處理,那么此時你可以用模板指定類型。比如你有一個正常的模板,但是當模板參數為double時想做一下特殊處理,那么我們可以再定義一個類型模板的類。這樣再生成模板時會優先生成最匹配的那一項
//普通模板
template<class T>
class A
{
public:A(){cout << "T()" << endl; }
};//特化
template<>
class A<double>
{
public:A(){cout << "double()" << endl;}
};int main()
{A<int> ai;A<char> ac;A<double>ad;return 0;
}
我們來看看它們會調用哪些構造函數。
我們發現,當在其他模板參數類型時會調用T(),但是當模板參數為double時,調用的是double()。 這也就意味著當模板參數為double時,那么會優先選擇最匹配的。這種行為被稱為特化。
半特化(偏特化)
當你要傳入的模板參數,一半需要自動推導,一半又需要自己定義時,可以使用半特化。
template<class T1,class T2>
class A
{
public:A(){cout << "A<T1,T2>" << endl; }
};template<class T1>
class A<T1,char>
{
public:A(){cout << "A<T1,char>" << endl;}
};int main()
{A<int,int> a1;A<double,char> a2;A<char,char>a3;return 0;
}
運行結果:
我們發現,只要一個模板參數對應,那么就會優先選擇對應的那個。
模板原理
模板的原理其實就是根據你所傳的模板參數,又給你自動生成了一個類。而這個過程在編譯時發生。就比如如下這個代碼。
template<class T1,class T2>
class A
{
public:T1 a;T2 b;
};int main()
{A<int,int> a1;A<double,char> a2;A<char,char>a3;return 0;
}
它在編譯后實際上是這樣的
class A
{
public:int a;int b;
};class A
{
public:double a;char b;
};class A
{
public:char a;char b;
};
會生成三個A類。分別對應的模板參數
A<int,int> a1;
A<double,char> a2;
A<char,char>a3;
當然,我們在編碼的時候是無法用相同的類名的。但在編譯時,會有類似于函數重載的機制(個人猜測)生成出多個類。
所以,函數模板的本質就是方便你編碼。等編譯時再為你自動生成你當初傳入的模板參數時對應的類。也就是說,雖然表面上你寫的代碼減少了,但本質上需要的代碼并沒有減少。只是在編譯時為你自動生成了。