模板——函式模板、類模板
模板
函式模板使程式設計師能夠用單段程式碼指定相關(過載)函式的全部範圍,稱為函式模板特殊化;
類模板使程式設計師能夠用單段程式碼指定相關類的全部範圍,稱為類模板特殊化。
什麼是泛型程式設計?
STL方法允許編寫通用的程式,使得程式碼不依賴於底層的容器。這種程式設計風格被稱為泛型程式設計。泛型程式設計以獨立於任何特定型別的方式編寫程式碼,泛型程式設計與面向物件程式設計一樣,都依賴於某種形式的多型性。
函式模板特殊化和類模板特殊化,這種技術就是泛型程式設計。
函式模板
過載函式通常用於對不同的資料型別執行相似的操作,不同資料型別的程式邏輯可能有所不同。如果每種資料型別的程式邏輯和操作都相同,則可以使用函式模板來更緊湊、更方便地實現函式過載。
就本質而言,定義一個函式模板就定義了一群過載函式。
所有的函式模板定義都從關鍵字template開始,後接它的模板引數表,列表位於一對尖括號(<和>)中。表示型別的每個模板引數,其前面都必須帶關鍵字class或template(二者可以互換),例如:
template < typename T> 或
template < class ElementType> 或
template < typename BorderType, typename FillType>
函式模板定義中的型別模型引數,用於指定函式實參的型別。函式的返回型別或宣告函式內部的變數。除此之外,函式定義與其他任何函式定義的形式相同。
下面看一個demo:printArray函式模板
#include <iostream> using namespace std; //定義函式模板 template <typename T> void printArray(const T* const array,int count) { for (int i=0; i<count; i++) { cout<<array[i]<<" "; } cout<<endl; }//函式模板定義結束 int _tmain(int argc, _TCHAR* argv[]) { const int ACOUNT = 5; const int BCOUNT = 7; const int CCOUNT = 6; int a[ACOUNT] = {1,2,3,4,5}; double b[BCOUNT] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7}; char c[CCOUNT] = "HELLO"; //通過呼叫printArray輸出每個陣列 //編譯器會使用它的過載解析功能找到與這個函式呼叫最為匹配的printArray函式定義 cout<<"Array a contains:"<<endl; printArray(a,ACOUNT); //用int*型別的第一個實參a呼叫 cout<<"Array b contains:"<<endl; printArray(b,BCOUNT); //用double*型別的第一個實參b呼叫 cout<<"Array c contains:"<<endl; printArray(c,CCOUNT); //用char*型別的第一個實參c呼叫 cout<<endl; system("pause"); return 0; }
執行結果:(這裡HELLO後面多了一個a,不知道是什麼原因,誰知道給我說下)
在這個例子中,採用模板機制使得不必像下面這樣編寫三個不同的過載函式的函式原型:
void printArray(const int*,int);
void printArray(const double*,int);
void printArray(const char*,int);
它們都使用了相同的程式碼,唯一的區別是型別T
類模板
類模板稱為引數化型別,因為它需要一個或者多個型別引數來指定如何定製一個“泛型類”模板,以形成一個類模板特殊化。
為了產生各種類模板特殊化,只需編寫一個類模板定義。每次需要一個額外的類模板特殊化時,只需要使用一種清晰、簡單的方法,編譯器就能為所需要的類模板特殊化編寫原始碼。
Demo:例如一個Stack類模板,可以是程式中建立許多Stack類的基礎,如:"Stack of double"、"Stack of int"、"Stack of char"、"Stack of Employee"等等
建立類模板Stack<T>
stack.h :
#ifndef STACK_H
#define STACK_H
template <typename T> //指定一個使用引數型別T的類模板定義
class stack
{
public:
stack(int = 10); //預設建構函式,棧大小為10
~stack()
{
delete[] stackPtr;
}
bool push(const T &); //把一個函式壓入棧
bool pop(T &); //從棧中取出一個函式
//判斷棧是不是空
bool isEmpty() const
{
return top == -1;
}
//判斷棧是不是滿了
bool isFull() const
{
return top == size - 1;
}
private:
int size;
int top;
T *stackPtr;
};
//constructor template
template <typename T>
stack<T>::stack(int s):size(s>0 ? s:10),top(-1),stackPtr(new T[size]){}
template<typename T> //出現在類模板定義之外的成員函式均以template<typename T> 開始
bool stack<T>::push(const T &pushvalue)
{
if ( !isFull() )
{
stackPtr[++top] = pushvalue;
return true;
}
return false;
}
template<typename T>
bool stack<T>::pop(T &popvalue)
{
if ( !isEmpty())
{
popvalue = stackPtr[top--];
return true;
}
return false;
}
#endif
main函式:
#include <iostream>
using namespace std;
#include "stack.h" //棧類模板定義
int _tmain(int argc, _TCHAR* argv[])
{
stack<double> doubleStack(5); //例項化一個大小為5的doubleStack物件
double doubleValue = 1.1;
cout<<"Pushing elements onto doubleStack\n";
while (doubleStack.push(doubleValue)) //將double值1.1,2.2,3.3,4.4和5.5壓入doubleStack中
{
cout<<doubleValue<<' ';
doubleValue += 1.1;
}
cout<<"\nStack is full.Cannot push "<<doubleValue
<<"\n\nPoping elements from doubleStack\n";
//pop elements from doubleStack
//在一個while迴圈中呼叫pop函式,從堆疊中移走這5個值
while (doubleStack.pop(doubleValue))
{
cout<<doubleValue<<' ';
}
cout<<"\nStack is empty,Cannot pop\n";
stack<int> intStack; //例項化了整數堆疊intStack,由於沒有指定大小,因此它的大小就是預設建構函式,這裡為10
int intValue = 1;
cout<<"\nPushing elements onto intStack\n";
//迴圈呼叫push函式,將一些值壓入intStack中,直到棧滿
while (intStack.push(intValue))
{
cout<<intValue++<<' ';
}
cout<<"\nStack is full.Cannot push "<<intValue
<<"\n\nPoping elements from intStack\n";
//呼叫pop函式,將這些值從intStack中移走,直到棧空。
while (intStack.pop(intValue))
{
cout<<intValue<<' ';
}
cout<<"\nStack is empty.Cannot pop"<<endl;
system("pause");
return 0;
}
執行結果:
參考資料:《c++程式設計師教程》 電子工業出版社 張良華等譯 P151-153,P420-426