C++模板剖析:函数模板、类模板解析
C++中關(guān)于模板&泛型編程問題:
?
問題引入:何編寫一個通用加法函數(shù)?
?
(1)使用函數(shù)重載,針對每個所需相同行為的不同類型重新實現(xiàn)它
int Add(const int &_iLeft, const int&_iRight)
{
return (_iLeft +_iRight);
}
float Add(const float &_fLeft, constfloat &_fRight)
{
return (_fLeft +_fRight);
}
【缺點】
1、只要有新類型出現(xiàn),就要重新添加對應(yīng)函數(shù)。
2、除類型外,所有函數(shù)的函數(shù)體都相同,代碼的復(fù)用率不高
3、如果函數(shù)只是返回值類型不同,函數(shù)重載不能解決
4、一個方法有問題,所有的方法都有問題,不好維護。
?
(2)使用公共基類,將通用的代碼放在公共的基礎(chǔ)類里面
?
【缺點】
1、借助公共基類來編寫通用代碼,將失去類型檢查的優(yōu)點;
2、對于以后實現(xiàn)的許多類,都必須繼承自某個特定的基類,代碼維護更加困難。
?
(3)使用特殊的預(yù)處理程序
#define ADD(a, b) ((a) + (b))
【缺點】
不是函數(shù),不進行參數(shù)類型檢測,安全性不高
?
綜上所述的問題,我們需要引入泛型編程,即為需要的函數(shù)或者類編寫一個模板,在實用的時候?qū)嵗纯伞D敲?#xff0c;什么是泛型編程?什么是模板?
?
一、???泛型編程
泛型編程:編寫與類型無關(guān)的邏輯代碼,是代碼復(fù)用的一種手段。模板是泛型編程的基礎(chǔ)。
二、???函數(shù)模板
函數(shù)模板:代表了一個函數(shù)家族,該函數(shù)與類型無關(guān),在使用時被參數(shù)化,根據(jù)實參類型產(chǎn)生函數(shù)的特定類型版本。
模板函數(shù)定義的格式:template<typename T1, teypename T2, ……….typename Tn>
函數(shù)返回值 函數(shù)名(參數(shù)列表)
{
?????? . . . . . .
}
?
Eg:
?????? template<typename T>
T Add( T left, T right )
{
?????? return left+right;
}
?????? template和typename 為關(guān)鍵字,T為模板形參的名字,可隨意命名。
?
typename是用來定義模板參數(shù)關(guān)鍵字,也可以使用class。建議盡量使typename。
??????
?????? 實例化:模板是一個藍圖,它本身不是類或者函數(shù),編譯器用模板產(chǎn)生指定的類或者函數(shù)的特定類型版本,產(chǎn)生模板特定類型的過程稱為函數(shù)模板實例化
注:模板被編譯了兩次:
① 實例化之前,檢查模板代碼本身,查看是否出現(xiàn)語法錯誤,如:遺漏分號
②在實例化期間,檢查模板代碼,查看是否所有的調(diào)用都有效,如:實例化類型不支持某些函數(shù)調(diào)用
?
實參推演:
從函數(shù)實參確定模板形參類型和值的過程稱為模板實參推斷,多個類型形參的實參必須完全匹配
類型形參轉(zhuǎn)換:
一般不會轉(zhuǎn)換實參以匹配已有的實例化,相反會產(chǎn)生新的實例。
編譯器只會執(zhí)行兩種轉(zhuǎn)換:
1、const轉(zhuǎn)換:接收const引用或者const指針的函數(shù)可以分別用非const對象的引用或者指針來調(diào)用
?
2、數(shù)組或函數(shù)到指針的轉(zhuǎn)換:如果模板形參不是引用類型,則對數(shù)組或函數(shù)類型的實參應(yīng)用常規(guī)指針轉(zhuǎn)換。數(shù)組實參將當做指向其第一個元素的指針,函數(shù)實參當做指向函數(shù)類型的指針。
?
Eg:
template<typename T>
void FunTest1(const T* t)
{
? cout<<"FunTest1();"<<*t<<endl;
}
template<typename T>
void FunTest2(const T& t)
{
?? cout<<"FunTest2();"<<t<<endl;
}
template<typename T>
void FunTest3(T t1, T t2)
{
? cout<<"FunTest3()"<<endl;
}
?
int Add(int a, int b)
{
? return a+b;
}
int main()
{
? int A = 10;
? int* pA = &A;
? FunTest1(pA);
?
? int b = 20;
? int& pB = b;
? FunTest2(pB);
?
? int array1[10];
? int array2[20];
?
? FunTest3(array1, array2);
? FunTest3(Add,Add);
? system("pause");
? return 0;
}
?
?
?????? 模板參數(shù):函數(shù)模板有兩種類型參數(shù):模板參數(shù)和調(diào)用參數(shù)。
?
(1)???模板形參:
a、模板形參名字只能在模板形參之后到模板聲明或定義的末尾之間使用,遵循名字屏蔽規(guī)則。
b、模板形參的名字在同一模板形參列表中只能使用一次。
Template<typename T, typenameT>
Void FunTest(T t1, T t2)
{};
?
c、 所有模板形參前面必須加上class或者typename關(guān)鍵字修飾,并且兩個關(guān)鍵字可以混用。
d、在函數(shù)模板的內(nèi)部不能指定缺省的模板實參。
Template<typename T>
T Add(T = int , T)?? //編譯出錯。
{};
非模板類型參數(shù):
?
非模板類型形參是模板內(nèi)部定義的常量,在需要常量表達式的時候,可以使用非模板類型參數(shù)。
?
?
????????????? 注:
1、模板形參表使用<>括起來。
2、和函數(shù)參數(shù)表一樣,跟多個參數(shù)時必須用逗號隔開,類型可以相同也可以不相同。
template<typenameT, typename V …….>
?
3、模板形參表不能為空(模板特化的時候,模板參數(shù)為空)。
?
4、模板形參可以是類型形參,也可以是非類型新參,類型形參跟在class和typename后
?
5、模板類型形參可作為類型說明符用在模板中的任何地方,與內(nèi)置類型或自定義類型使用方法完全相同,可用于指定函數(shù)形參類型、返回值、局部變量和強制類型轉(zhuǎn)換。
?
6、模板形參表中,class和typename具有相同的含義,可以互換,使用typename更加直觀。但關(guān)鍵字typename是作為C++標準加入到C++中的,舊的編譯器可能不支持。
?
?????? 模板函數(shù)重載:
?
注:
1、函數(shù)的所有重載版本的聲明都應(yīng)該位于該函數(shù)被調(diào)用位置之前
2、一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,而且該函數(shù)模板還可以被實例化為這個非模板函數(shù)。
3、對于非模板函數(shù)和同名函數(shù)模板,如果其他條件都相同,在調(diào)動時會優(yōu)先調(diào)動非模板函數(shù)而不會從該模板產(chǎn)生出一個實例。如果模板可以產(chǎn)生一個具有更好匹配的函數(shù),那么將選擇模板。
4、顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個調(diào)用,而且所有的模板參數(shù)都應(yīng)該根據(jù)實參演繹出來。
5、模板函數(shù)不允許自動類型轉(zhuǎn)換,但普通函數(shù)可以進行自動類型轉(zhuǎn)換。
?
新的問題引入:
string s1 = "addfhgj";
??? string s2 = "addfghjkl";
Max(s1,s2); //未能從“const std::string”為“const std::move_iterator<_RanIt> &”推導(dǎo) 模板 參數(shù)。
故模板有一些特殊的情況不能處理,就需要引入模板的特化,什么是模板的特化?
?
?????? 模板函數(shù)特化:
?
有時候并不總是能夠?qū)懗鰧λ锌赡鼙粚嵗念愋投甲詈线m的模板,在某些情況下,通用模板定義對于某個類型可能是完全錯誤的,或者不能編譯,或者做一些錯誤的事情。
?
Eg:比較兩個字符串的大小
?????? 調(diào)試之后發(fā)現(xiàn)比較的是字符串地址的大小而不是字符串的大小。因此需要對模板函數(shù)進行特化以處理特殊的情況。這就需要對模板類的特殊情況進行處理-------模板特化:
注意:
在模板特化版本的調(diào)用中,實參類型必須與特化版本函數(shù)的形參類型完全匹配,如果不匹配,編譯器將為實參模板定義中實例化一個實例。
特化不能出現(xiàn)在模板實例的調(diào)用之后,應(yīng)該在頭文件中包含模板特化的聲明,然后使用該特化版本的每個源文件包含該頭文件。
?
三、???模板類
1、????? 模板類的定義格式:
?
template<class 形參名1, class 形參名2, ...class 形參名n>
class 類名
{ ... };
?
// 使用模板方式實現(xiàn)動態(tài)順序表
template<typename T>
class SeqList
{
public :
SeqList();
~ SeqList();
private :
int _size ;
int _capacity ;
T* _data ;
};
?
?
template <typename T>
SeqList <T>:: SeqList()
: _size(0)
, _capacity(10)
, _data(new T[ _capacity])
{}
template <typename T>
SeqList <T>::~ SeqList()
{
delete [] _data ;
}
?
void Test()
{
???????? SeqList<int>s1;
???????? SeqList<double>s2;
???????? SeqList<char>s3;
}
?
?
?
2、?? 模板類的實例化
只要有一種不同的類型,編譯器就會實例化出一個對應(yīng)的類。
SeqList<int > sl1;
SeqList<double > sl2;
當定義上述兩種類型的順序表時,編譯器會使用int和double分別代替模板形參,重新編寫SeqList類,最后創(chuàng)建名為SeqList<int>和SeqList<double>的類。
?
(1)模板參數(shù)實現(xiàn)容器適配器。
#include "List.h"
//底層使用List 容器
?
template <class T, class container = list<T>> //模板參數(shù)
class Queue
{
public:
? void push(const T& x)
? {
???? return _con.PushBack(x);
? }
? void pop()
? {
???? return _con.PopFront();
? }
? const T& GetHead()
? {
???? return _con.Front();
? }
? const T& GetTail()
? {
???? return _con.Back();
? }
? bool IsEmpty()
? {
???? return _con.Empty();
? }
private:
? container _con;
};
void Test2()
{
? Queue<int> q1;//使用缺省的模板參數(shù)構(gòu)造對象
? Queue<int, List<int>> q2;//使用模板參數(shù)構(gòu)造對象
}
(2)模板的模板參數(shù)實現(xiàn)容器適配器。
??? template <class T, template<class>? class container = List> //使用模板的模板參數(shù)
class Queue
{
public:
? void push(const T& x)
? {
???? return _con.PushBack(x);
? }
? void pop()
? {
???? return _con.Pop();
? }
? const T& GetHead()
? {
???? return _con.Front();
? }
? const T& GetTail()
? {
???? return _con.Back();
? }
? bool IsEmpty()
? {
???? return _con.Empty();
? }
private:
? container<T>? _con;
};
void Test1()
{
Queue<int>q1;//使用缺省的模板類的模板參數(shù)構(gòu)造對象
? Queue<int,List> q2;//使用模板的模板參數(shù)構(gòu)造不同類型對象
}
?
(3)非類型的類模板參數(shù)
template <typename T, size_t MAX_SIZE = 10>//帶缺省模板參數(shù)
//template<typename T, double MAX_SIZE = 10.0> // “double”: 非類型模板參數(shù)“MAX_SIZE”的類型非法
?
class Array
{
public :
? Array();
private :
? T _array [MAX_SIZE];
int _size ;
};
template <typename T, size_t MAX_SIZE>
Array <T,MAX_SIZE>::Array()
? : _size(0)
? {}
?
void Test()
{
? Array<int> a1;
? Array<int , 20> a2;
}
(4)類模板的特化:
//順序表類的部分實現(xiàn)
template <typename T>
class SeqList
{
public :
??? SeqList();
??? ~ SeqList();
private :
??? int _size ;
??? int _capacity ;
??? T* _data ;
};
template<typename T>
SeqList <T>:: SeqList()
??? : _size(0)
??? , _capacity(10)
??? , _data(new T[ _capacity])
??? {
??????? cout<<"SeqList<T>:: SeqList()" <<endl;
??? }
template<typename T>
SeqList <T>::~ SeqList()
{
??? delete[] _data ;
}
?
//全特化:
template <>
class SeqList <int>
{
public :
??? SeqList(int capacity);
??? ~ SeqList();
private :
??? int _size ;
??? int _capacity ;
??? int* _data ;
};
// 特化后定義成員函數(shù)、成員函數(shù)不再需要模板形參列表
SeqList <int>:: SeqList(int capacity)
??? : _size(0)
??? , _capacity(capacity )
??? , _data(new int[ _capacity])
??? {
??????? cout<<"SeqList<int>" <<endl;
??? }
SeqList <int>::~ SeqList()
{
??? delete[] _data ;
}
void test1 ()
{
??? SeqList<double > sl2;
??? SeqList<int > sl1(2);
}
?
//偏特化(部分特化)
?
template <typename T1, typename T2>
class Data
{
public :
??? Data();
};
template <typename T1, typename T2>
Data<T1 , T2>::Data()
{
??? cout<<"Data<T1,T2>"<<endl;
}
// 局部特化第二個參數(shù)為某個具體的類型,如int 、 double 等
template <typename T1>
class Data <T1, int>
{
public :
??? Data();
?
};
template <typename T1>
Data<T1 , int>::Data()
{
cout<<"Data<T1,int>"<<endl;
}
?
下面的例子可以看出,偏特化并不僅僅是指特化部分參數(shù),而是針對模板參數(shù)更進一步的條件限制所設(shè)計出來的一個特化版本。
?
// 局部特化兩個參數(shù)為指針類型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public :
??? Data();
?
};
template <typename T1, typename T2>
Data<T1 *, T2*>:: Data()
{
??? cout<<"Data<T1*,T2*>"<<endl;
}
// 局部特化兩個參數(shù)為引用
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public :
??? Data(const T1& d1, const T2& d2);
?
};
template <typename T1, typename T2>
Data<T1 &, T2&>:: Data(const T1& d1, const T2& d2)
??? {
?????? cout<<"Data<T1&,T2&>"<<endl;
??? }
void Test()
{
??? Data<double , int> d1;
??? Data<int , double> d2;
??? Data<int *, int*> d3;
??? Data<int&, int&> d4(1, 2);
}
?
總結(jié)
以上是生活随笔為你收集整理的C++模板剖析:函数模板、类模板解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图像去雾算法学习
- 下一篇: python3图片转代码_python3