人們需要編寫多個形式和功能都相似的函數,因此有了函數模板來減少重複勞動;人們也需要編寫多個形式和功能都相似的類,於是 C++ 引人了類模板的概念,編譯器從類模板可以自動生成多個類,避免了程序員的重複勞動。
例如,在《C++運算符重載》一章中的《C++實現可變長度的動態數組》一節中,我們實現了一個可變長的整型數組類,可能還需要可變長的 double 數組類,可變長的 CStudent 數組類,等等。如果要把類似於可變長整型數組類的代碼都重寫一遍,無疑非常麻煩。有了類模板的機制,只需要寫一個可變長的數組類模板,編譯器就會由該類模板自動生成整型、double 型等各種類型的可變長數組類了。
語法:
template <類型參數表>
class 類模板名{
成員函數和成員變量
};
參數:
class類塑參數1, class類型參數2, ...
類模板中的成員函數放到類模板定義外面寫時的語法如下:
template <類型參數表>
返回值類型 類模板名<類型參數名列表>::成員函數名(參數表)
{
...
}
用類模板定義對象的寫法如下:
類模板名<真實類型參數表> 對象名(構造函數實際參數表);
如果類模板有無參構造函數,那麼也可以使用如下寫法:
類模板名 <真實類型參數表> 對象名;
類模板看上去很像一個類。下面以 Pair 類模板爲例來說明類模板的寫法和用法。
實踐中常常會碰到,某項數據記錄由兩部分組成,一部分是關鍵字,另一部分是值。關鍵字用來對記錄進行排序和檢索,根據關鍵字能查到值。例如,學生記錄由兩部分組成,一部分是學號,另一部分是績點。要能根據學號對學生進行排序,以便方便地檢索績點,則學號就是關鍵字,績點就是值。
下面的Pair類模板就可用來處理這樣的數據記錄:
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
public:
T1 key; //關鍵字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成員函數 operator <
{ //"小"的意思就是關鍵字小
return key < p.key;
}
int main()
{
Pair<string,int> student("Tom",19); //實例化出一個類 Pair<string,int>
cout << student.key << " " << student.value;
return 0;
}
程序的輸出結果是:
Tom 19
實例化一個類模板時,如第 21 行,真實類型參數表中的參數是具體的類型名,如 string、int 或其他類的名字(如 CStudent)等,它們用來一一對應地替換類模板定義中“類型參數表”中的類型參數。類模板名 <真實類型參數表>
就成爲一個具體的類的名字。
編譯器編譯到第 21 行時,就會用 string 替換 Pair 模板中的 T1,用 int 替換 T2,其餘部分原樣保留,這樣就自動生成了一個新的類。這個類的名字編譯器是如何處理的不需要知道,可以認爲它的名字就是 Pair <string, int>。也可以說,student 對象的類型就是 Pair<string, int>。
Pair<string, int> 類的成員函數自然也是通過替換 Pair 模板的成員函數中的 T1、T2 得到的。
編譯器由類模板生成類的過程叫類模板的實例化。由類模板實例化得到的類叫模板類。
函數模板作爲類模板成員:
類模板中的成員函數還可以是一個函數模板。成員函數模板只有在被調用時纔會被實例化。例如下面的程序:
#include <iostream>
using namespace std;
template <class T>
class A
{
public:
template <class T2>
void Func(T2 t) { cout << t; } //成員函數模板
};
int main()
{
A<int> a;
a.Func('K'); //成員函數模板Func被實例化
a.Func("hello");
return 0;
}
程序的輸出結果是:
Khello