C++模板初階(泛型程式設計,函式模板,類模板)
1.泛型程式設計
怎麼實現一個通用的交換函式呢
使用函式過載雖然可以實現,但是有一下幾個不好的地方:
1. 過載的函式僅僅只是型別不同,程式碼的複用率比較低,只要有新型別出現時,就需要增加對應的函式
2. 程式碼的可維護性比較低,一個出錯可能所有的過載均出錯
那能否告訴編譯器一個模子,讓編譯器根據不同的型別利用該模子來生成程式碼呢?
所以就引出了泛型程式設計的概念
泛型程式設計:編寫與型別無關的通用程式碼,是程式碼複用的一種手段。模板是泛型程式設計的基礎
模板分為:
1.函式模板
2.類模板
2.函式模板
2.1函式模板的概念:
函式模板代表了一個函式家族,該函式模板與型別無關,在使用時被引數化,根據實參型別產生函式的特定型別版本
2.2 函式模板的格式:
template<typename T1, typename T2,......,typename Tn>
返回值型別 函式名(引數列表){}
注意:typename是用來定義模板引數關鍵字,也可以使用class(切記:不能使用struct代替class)
2.3函式模板的原理
模板是一個藍圖,它本身並不是函式,是編譯器用使用方式產生特定具體型別函式的模具。所以其實模板就是將本來應該我們做的重複的事情交給了編譯器
在編譯器編譯階段,對於模板函式的使用,編譯器需要根據傳入的實參型別來推演生成對應型別的函式以供 呼叫。比如:當用double型別使用函式模板時,編譯器通過對實參型別的推演,將T確定為double型別,然 後產生一份專門處理double型別的程式碼,對於字元型別也是如此。
2.4函式模板的例項化
用不同型別的引數使用函式模板時,稱為函式模板的例項化。模板引數例項化分為:隱式例項化和顯示例項化
1.隱式例項化:讓編譯器根據實參推演模板引數的實際型別
#include <iostream> using namespace std; template<class T> T Add(const T& left, const T& right) { return left + right; } int main() { int a1 = 10; int a2 = 20; double b1 = 10.0; double b2 = 20.0; Add(a1, a2); Add(b1, b2); //Add(a1, b1);//這句不能通過編譯,因為在編譯期間,當編譯器看到 //該例項化時,需要推演其實參型別通過實參a1將T推演 //為int,通過實參d1將T推演為double,但模板引數列表 //只有一個T,編譯器無法確定此處到底該將T確定為int //或者double型別 //注意:在模板中,編譯器一般不會進行類別轉換操作,因為一旦轉換出了問題,編譯器就要背鍋 //此時有兩種方式:1.使用者自己來強制轉化 2.使用顯式例項化 Add(a1, (int)b1); return 0; }
2.顯式例項化:在函式後的<>中指定模板引數的實際型別
int main()
{
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>
T Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2);//與非模板函式匹配,不需要函式模板例項化
Add(1, 2.0);//模板函式可以生成更加匹配的版本,編譯器
//根據實參生成更加匹配的Add函式
}
3.顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個呼叫,而且所有的模板引數都應該根據實參演繹出來
//專門處理int的加法函式
int Add(int left, int right)
{
return left + right;
}
//通用加法函式
template<class T,class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2);//與非模板函式匹配,不需要函式模板例項化
Add<>(1,2);//呼叫模板生成的Add函式
}
4.模板函式不允許自動型別轉換,但普通函式可以進行自動型別轉換
3.類模板
3.1類模板的定義格式
template<class T1,class T2,...,class Tn>
class 類模板名
{
//類內成員定義
};
//動態順序表
template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
:_pData(new T[capacity])
, _size(0);
, _capacity(capacity)
{}
//在類中宣告,在類外定義
~Vector();
void PushBack(const T& data)
{
//_checkCapacity();
_pData[_size++] = data;
}
void PopBack()
{
--_size;
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
//注意,類模板中函式放在類外進行定義時,需要加模板版引數列表
template<class T>
Vector<T>::~Vector()
{
if (_pData)
{
delete[] _pData;
}
}
3.2 類模板的例項化
類模板例項化與函式模板例項化不同,類模板例項化需要在類模板名字後面跟<>,然後將例項化的型別放在<>中即可,類模板的名字不是真正的類,而例項化的結果才是真正的類。
//Vector類名,Vector<int>才是型別
Vector<int> s1;
s1.PushBack(1);
s1.PushBack(2);
Vector<double> s2;
s2.PushBack(1.0);
s2.PushBack(2.0);