C++超詳細(xì)講解泛型
1.了解泛型編程
就好比活字印刷術(shù),可以靈活調(diào)整印刷的板塊和內(nèi)容,比只能固定印刷某一個(gè)內(nèi)容的雕版印刷術(shù)效率更高,也讓印刷術(shù)由此得到了更廣泛的應(yīng)用。

在C++中,函數(shù)重載和模板的出現(xiàn),讓泛型編程得到了實(shí)際的應(yīng)用。其中模板,就是類似活字印刷術(shù)一樣的存在。
2.函數(shù)模板
八八了那么多沒(méi)用的,讓我們來(lái)看看函數(shù)模板的語(yǔ)法實(shí)現(xiàn)吧
2.1簡(jiǎn)單示例
下面是一個(gè)最簡(jiǎn)單的交換函數(shù)的例子,通過(guò)標(biāo)明模板參數(shù)T,讓編譯器自動(dòng)識(shí)別函數(shù)傳參,并調(diào)用出不同的函數(shù)
template<typename T>
void Swap(T& left,T& right)
{
T temp = left;
left = right;
right = temp;
}
其中,typename是定義模板的關(guān)鍵字,我們可以使用class來(lái)替代,但不能使用struct

可以看到,編譯器成功調(diào)用了Swap函數(shù),交換了int類型和double類型
2.2多個(gè)模板參數(shù)
如果我們嘗試把int和double同時(shí)傳參給這個(gè)函數(shù),會(huì)發(fā)生什么呢?

編譯器會(huì)報(bào)錯(cuò),表示模板參數(shù)T不明確
這時(shí)候我們有幾種解決方法
首先是將double強(qiáng)轉(zhuǎn)為int(反過(guò)來(lái)亦可)

你會(huì)發(fā)現(xiàn)還是不行,那是因?yàn)閺?qiáng)轉(zhuǎn)并不支持用double引用int。所以我們把函數(shù)傳參中的引用去掉,即可正常調(diào)用這個(gè)函數(shù)(暫且不提傳引用和傳值的區(qū)別)

使用多個(gè)模板參數(shù)
和函數(shù)傳參類似,我們也可以設(shè)置多個(gè)模板參數(shù)
在下圖中,我使用typeid關(guān)鍵字來(lái)打印模板參數(shù)T1和T2的類型。
使用typeid需要包含頭文件#include <typeinfo>

可以看到,實(shí)際上函數(shù)在調(diào)用這個(gè)模板的時(shí)候,已經(jīng)實(shí)例化了這個(gè)函數(shù)(即替換模板參數(shù)為正確參數(shù)類型)這時(shí)候在后臺(tái)處理的時(shí)候,其實(shí)Show函數(shù)已經(jīng)實(shí)例化為了下面這個(gè)樣子
void Show(int left, double right)
{
cout << typeid(left).name() << endl;
cout << typeid(right).name() << endl;
}
2.3模板實(shí)例化
上面的方式,是編譯器自動(dòng)幫我們實(shí)例化模板參數(shù)。在實(shí)際使用中,我們還可以自己指定實(shí)例化為什么類型
- 利用強(qiáng)制類型轉(zhuǎn)換
- 使用
<int>直接指定實(shí)例化為int類型

使用第二種方式的時(shí)候,編譯器會(huì)對(duì)另外一個(gè)不匹配的參數(shù)進(jìn)行隱式類型轉(zhuǎn)換。如果轉(zhuǎn)換不成功,則會(huì)報(bào)錯(cuò)。
另外注意的是,函數(shù)模板參數(shù)T同樣可以用來(lái)作為返回值,但是不能通過(guò)返回值來(lái)推斷參數(shù)T的類型。比如下面這個(gè)函數(shù),我們?cè)谑褂玫臅r(shí)候就需要直接指定模板參數(shù)T,而不能寫一個(gè)int* ptr=test(10)讓編譯器通過(guò)“返回值是int*接收的,所以函數(shù)模板參數(shù)T是int”來(lái)推斷。
template<typename T>
T* test(int num)
{
return new T[num];
}
函數(shù)模板支持給予參數(shù)缺省值
當(dāng)一個(gè)參數(shù)不確定的時(shí)候,函數(shù)模板是支持給予缺省值的
template<typename T=char>
T* test(int num)
{
return new T[num];
}
比如這樣,當(dāng)我們沒(méi)有直接指定的時(shí)候,編譯器就會(huì)將T作為char類型,返回一個(gè)num大小的char(一個(gè)字節(jié))的空間
注意:當(dāng)有多個(gè)模板參數(shù)時(shí),缺省值需要從右往左給
函數(shù)模板的傳參也支持缺省值
template<typename T1>
void Add(T1 left, T1 right=10)
{
cout << "Add temp "<<typeid(left).name() << " " << typeid(right).name() << endl;
cout << left + right << endl << endl;
}
int main()
{
int a=1;
Add(a);
}
在這種情況下,編譯器會(huì)正確調(diào)用該函數(shù)模板

2.4模板和普通函數(shù)同時(shí)存在
以Add函數(shù)為例,在函數(shù)模板存在的同時(shí),我們還可以單獨(dú)寫一個(gè)int類型的add函數(shù)。這都?xì)w功于函數(shù)重載的存在。
同時(shí),我們還可以使用<int>來(lái)指定函數(shù)模板重載為已存在的Add函數(shù)。因?yàn)楸举|(zhì)上這兩個(gè)函數(shù)是不同的,并不會(huì)沖突。

函數(shù)在調(diào)用的時(shí)候,首先會(huì)去調(diào)用已經(jīng)存在的函數(shù)。當(dāng)參數(shù)和已存在的函數(shù)不匹配時(shí),才會(huì)調(diào)用函數(shù)模板
2.5函數(shù)模板不支持定義和聲明分離
一般情況下,我們都會(huì)在頭文件中生命函數(shù),在另外一個(gè)源文件中定義函數(shù)。
但是模板是不支持這么做的!編譯器會(huì)報(bào)錯(cuò) 鏈接錯(cuò)誤
error LNK2019:無(wú)法解析的外部符號(hào)……
所以我們需要將函數(shù)模板的聲明和定義放在一個(gè)頭文件中。在部分使用場(chǎng)景,會(huì)使用.hpp來(lái)表示這個(gè)頭文件是包含了函數(shù)定義的(即.h和.cpp的集合體)。需要注意,這并不是一個(gè)硬性要求,你也可以直接使用.h,并將聲明和定義放入其中。
這是為什么呢?
因?yàn)閱为?dú)的.h聲明會(huì)在源文件頂部展開(kāi),而此時(shí)函數(shù)模板正常推演參數(shù),但編譯器并沒(méi)有找到函數(shù)的實(shí)現(xiàn),即這是一個(gè)沒(méi)有地址的函數(shù)。從而導(dǎo)致編譯器找不到函數(shù)的地址,產(chǎn)生了符號(hào)表的鏈接錯(cuò)誤
有無(wú)解決辦法?
其實(shí)是有的,我們可以在模板函數(shù)定義的.cpp中對(duì)我們需要使用的函數(shù)進(jìn)行顯式實(shí)例化指定
//頭文件
//聲明
template<typename T1>
void Add(T1 left, T1 right);
//源文件
//定義
template<typename T1>
void Add(T1 left, T1 right)
{
cout << left + right << endl << endl;
}
//在源文件中顯式實(shí)例化
template
void Add<int>(int left, int right);
template
void Add<double>(double left, double right);
顯式實(shí)例化需要對(duì)我們要用的所有函數(shù)進(jìn)行實(shí)例化,比如你需要用double類型,只顯示實(shí)例化了int類型是不行的,依舊會(huì)報(bào)錯(cuò)。
這樣感覺(jué)非常多余……對(duì)吧!所以還是老老實(shí)實(shí)把聲明和定義放在同一個(gè)文件里面吧!
3.類模板
類模板的基本形式如下,這里作為一個(gè)小區(qū)分,我用class來(lái)當(dāng)作模板參數(shù)名。實(shí)際上typename也是可以的
template<class T1, class T2, ...>class 類模板名{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->// 類內(nèi)成員定義}; template<class T1, class T2, ...>
class 類模板名
{
// 類內(nèi)成員定義
};
3.1簡(jiǎn)單示例
下面用一個(gè)非常簡(jiǎn)單的順序表代碼來(lái)演示一下類模板
template<class T>
class List
{
public:
List(int capacity = 10)
: _a(new T[capacity])
, _size(0)
, _capa(capacity)
{}
~List();
T& operator[](int pos)
{
assert(pos < _size);
return _a[pos];
}
private:
T* _a;
int _size;
int _capa;
};
//類模板中函數(shù)放在類外進(jìn)行定義時(shí),需要加模板參數(shù)列表
template <class T>
List<T>::~List()
{
delete[] _a;
_size = _capa = 0;
}
可以看到,通過(guò)顯式實(shí)例化的方式,我們成功讓這個(gè)類模板變成了兩個(gè)不同類型的順序表

3.2成員函數(shù)聲明和定義分離
其中需要注意的是析構(gòu)函數(shù),聲明和定義分離的時(shí)候(同一文件),在定義的時(shí)候也需要加上模板參數(shù)
//類模板中函數(shù)放在類外進(jìn)行定義時(shí),需要加模板參數(shù)列表
template <class T>
List<T>::~List()
{
delete[] _a;
_size = _capa = 0;
}
個(gè)人覺(jué)得這樣也非常麻煩,既然模板最好是聲明和定義放在同一個(gè)文件,那還不如直接將類的成員函數(shù)直接定義到類內(nèi)部。多省事!
如果是聲明和定義放在不同文件中,顯式實(shí)例化方式如下
template class List <int>; template class List <double>;
需要什么類型的類,就得實(shí)例化這個(gè)類型。
到此這篇關(guān)于C++超詳細(xì)講解泛型的文章就介紹到這了,更多相關(guān)C++泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用while判斷輸入的數(shù)字是否回文數(shù)的簡(jiǎn)單實(shí)現(xiàn)
這篇文章主要介紹了用while判斷輸入的數(shù)字是否回文數(shù)的簡(jiǎn)單實(shí)現(xiàn),需要的朋友可以參考下2014-02-02
C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼
以下是對(duì)C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼進(jìn)行了分析介紹,需要的朋友可以參考下2013-07-07
C語(yǔ)言常見(jiàn)排序算法之插入排序(直接插入排序,希爾排序)
這篇文章介紹C語(yǔ)言常見(jiàn)排序算法之插入排序(直接插入排序,希爾排序),主要分享介紹的是插入排序的兩種常用算法,直接插入排序和希爾排序,需要的朋友可以參考一下2022-07-07
C++利用鏈表寫一個(gè)簡(jiǎn)單的棧實(shí)例詳解
這篇文章主要介紹了C++利用鏈表寫一個(gè)簡(jiǎn)單的棧實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
C++中用new創(chuàng)建二維數(shù)組和指針數(shù)組實(shí)例代碼
這篇文章主要介紹了C++中用new創(chuàng)建二維數(shù)組和指針數(shù)組實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-03-03
C語(yǔ)言之整數(shù)劃分問(wèn)題(遞歸法)實(shí)例代碼
這篇文章主要介紹了C語(yǔ)言之整數(shù)劃分問(wèn)題(遞歸法)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
C語(yǔ)言游戲必備:光標(biāo)定位與顏色設(shè)置的實(shí)現(xiàn)方法
本篇文章是對(duì)c語(yǔ)言中光標(biāo)定位與顏色設(shè)置的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

