嘿,各位技術潮人!好久不見甚是想念。生活就像一場奇妙冒險,而編程就是那把超酷的萬能鑰匙。此刻,陽光灑在鍵盤上,靈感在指尖跳躍,讓我們拋開一切束縛,給平淡日子加點料,注入滿滿的passion。準備好和我一起沖進代碼的奇幻宇宙了嗎?Let's go!
我的博客:yuanManGan
我的專欄:C++入門小館?C言雅韻集?數據結構漫游記? 閑言碎語小記坊?題山采玉?領略算法真諦
目錄
模板:
1.泛型編程:
2.函數模板
2.1函數模板概念:
2.2函數模板格式:
2.3 函數模板的原理
2.4 函數模板的實例化
1.隱式實例化:
2. 顯式實例化:
2.5 模板參數的匹配原則
3. 類模板
3.1 類模板的定義格式
3.2 類模板的實例化
4. 非類型模板參數
5.模板的特化
5.1函數模板的特化:
5.2類模板特化
5.2.1全特化
5.2.2?偏特化
6 模板分離編譯
7. 模板總結
模板:
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;
}
......
我們要實現交換,難道寫無數個類型的組合?
這里用函數重載有很多缺點:
代碼的可維護性差,多一個類型就要多寫幾個對應的代碼,復用率低
那我們是否可以告訴編譯器一個模子,讓編譯器自動生成對應的函數呢?
模板又分為函數模板和類模板:
2.函數模板
2.1函數模板概念:
2.2函數模板格式:
2.3 函數模板的原理
在編譯器的編譯階段,對于函數模板的使用,編譯器需要根據傳入的實參類型來推演生成對應類型的函數以供調用。比如:當用double類型使用函數模板時,編譯器通過對實參類型的推演,將T確定為double類型,然后產生一份專門處理double類型的代碼,對于字符類型也是如此。
2.4 函數模板的實例化
1.隱式實例化:

?該語句不能通過編譯,因為在編譯期間,當編譯器看到該實例化時,需要推演其實參類型
?通過實參a1將T推演為int,通過實參d1將T推演為double類型,但模板參數列表中只有一個T,
?編譯器無法確定此處到底該將T確定為int 或者 double類型而報錯注意:在模板中,編譯器一般不會進行類型轉換操作,因為一旦轉化出問題,編譯器就需要背黑鍋
Add(a1, (int)d1);
2. 顯式實例化:

如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯
2.5 模板參數的匹配原則


3. 類模板
3.1 類模板的定義格式
template<class T1, class T2, ..., class Tn>
class 類模板名
{
// 類內成員定義
};
#include<iostream>
using namespace std;
// 類模版
template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};
// 模版不建議聲明和定義分離到兩個文件.h 和.cpp會出現鏈接錯誤,具體原因后面會講
template<class T>
void Stack<T>::Push(const T& data)
{// 擴容_array[_size] = data;++_size;
}
int main()
{Stack<int> st1; // intStack<double> st2; // doublereturn 0;
}
在STL中大量使用類模板。
3.2 類模板的實例化
// Stack是類名,Stack<int>才是類型
Stack<int> st1; // int
Stack<double> st2; // double
4. 非類型模板參數
先看看下面的
假如我們要創建a1的存儲空間為100,a2的存儲空間為50。但我們實例化出來的對象它的存儲空間依舊是N,我們只能將就內存大的,這時就會出現空間浪費。那有沒有方法我們能自己控制N是多少呢?有的兄弟有的:
模板參數分為類型形參與非類型形參
類型形參即:出現在模板參數列表中,跟在class或者typename之類的參數類型名稱。
非類型形參,就是用一個常量作為類(函數)模板中可將改參數當成常量來使用。
非類型形參就可以解決這個問題:
那有沒有什么實例用到了這個呢?
有的,就是動態數組array
這里沒有尾部插入刪除。因為我們可以直接通過下標訪問最后的元素。
那你說這個和 array<int, 100> a?和 int a[100]有什么區別呢?
有人說array可以自定義類型比如:
array<string, 100> a;
那我問你,數組不能這樣嗎:
string a[20];
那我用vector不是更香嗎?
array和靜態數組沒有什么本質區別,非要說區別就是array有迭代器,但靜態數組也可以使用范圍for,本質的類型的轉換,然后,array對越界的檢查更嚴格:
越界讀 | 越界訪問 | |
靜態數組 | 不報錯 | 抽查報錯 |
array | 報錯 | 報錯 |
5.模板的特化
我們看看下面的代碼:
我們比較指針的時候是無法比較出我們想要的結果,那我們該怎么做呢,可以在Date類里面重載一下比較運算符當傳入的是指針的時候特殊處理一下。
那我們就是不想改變原類呢,怎么在模板上進行修改呢?
這里就可以使用我們的模板的特化。即:在原模板類的基礎上,針對特殊類型所進行特殊化的實現方式。模板特化中分為函數模板特化與類模板特化。
5.1函數模板的特化:

?注意:一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為了實現簡單通常都是將該 函數直接給出。
?直接寫函數比寫模板的特化要舒服很多。
bool Less(Date* left, Date* right)
{return *left < *right;
}
5.2類模板特化
5.2.1全特化
全特化即是將模板參數列表中所有的參數都確定化。
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;Data<int, char> d2;
}
這全特化出來的對象就和非模板函數和模板函數的關系一樣,有更匹配的調用更匹配的,沒有就匹配的就調用模板重新創建一個。
5.2.2?偏特化
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
我們可以對其中一個或多個參數特化
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
// 將第二個參數特化為int
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};
參數限制:
還可以指定傳入的類型,比如傳入指針就走這種類型:
//兩個參數偏特化為指針類型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;T2 _d2;
};
//兩個參數偏特化為引用類型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};
6 模板分離編譯
我們之前學習STL時說過使用了模板之后的類的函數聲明和定義最好不要分離。
我們先來了解一下這些:
這個是我們之前學習c語言時可能會困惑的問題,如果在頭文件寫聲明和定義會出現鏈接報錯。
因為在符號表中會重復包含add函數,test.cpp和func.cpp共用一個符號表。我們可以將聲明和定義分離到不同的頭文件,或者將函數靜態不出現在符號表。
我們將聲明的定義分離是怎么實現的呢,這里再來回顧一下: