C++_代碼重用5-類模板
如果兩種類只是數據類型不同,而其他代碼是相同的,與其編寫新的類聲明,不如編寫一種泛型(獨立於類型的)棧。然後將具體的類型作為參數傳遞給這個類。這樣就可以使用通用的代碼生成存儲不同類型值的棧。
可以使用typedef處理這樣的需求,但是有兩個問題,一、每次修改類型都必須重新編輯頭文件;二、在每個程序中都只能使用這種技術生成一種棧。
C++的類模板為生成通用的類聲明提供了一種更好的方法;模板提供參數化類型,能夠將類型名作為參數傳遞給接收方來建立類或函數。
C++標準模板庫(STL)提供了幾種功能強大而靈活的容器類模板。
定義模板類
template <class Type>,template告訴編譯器,將要定義一個模板。尖括號內容相當於函數的參數列表。
也可以這麽寫:
template <typename Type>
或是:
template <class T>
template <typename T>
當模板被調用時,Type將被具體的類型值(int或string)取代。
原來類的聲明:
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
int top;
public:
Stack( );
bool isempty( ) const;
bool isfull( ) const;
bool push (const Item & item);
bool pop(Item & item);
}
類模板信息必須都放在頭文件中,不能把模板成員函數放到獨立的實現文件中。因為模板不是函數,不能單獨編譯。在要使用這些模板的文件中包含該頭文件。
類模板:
template<class Type>
Stack<Type>::Stack()
{
}
template <class Type>
bool Stack<Type>::isempty()
{
}
template <class Type>
bool Stack<Type>::isfull()
{
}
template <class Type>
bool Stack<Type>::push(const Type & item)
{
}
template <class Type>
bool Stack<Type>::pop(Type & item)
{
}
使用類模板
僅在程序包含模板並不能生成模板類,而必須請求實例化。需要聲明一個類型為模板類的對象,並且使用所需的具體類型替換泛型名。
Stack<int> kernels;
Stack<string>colonels;
看到上述兩個聲明後,編譯器將按Stack<Type>模板來生成兩個獨立的類聲明和兩組獨立的類方法。
泛型標識符——Type——被稱為類型參數,這意味著它類似於變量,但是賦給它們的不能是數字,而只能是類型。
必須,顯式地提供所需的類型,這與常規的函數模板是不同的。
深入探討模板類
可以將內置類型或對象作為類模板Stack<Type>的類型。指針也是可以的。但是如果不對程序做重大修改,將無法很好地工作。
創建不同的指針是調用程序的職責,而不是棧的職責。棧的任務是管理指針,而不是創建指針。
字符串本身永遠不會移動,把字符串壓入棧實際上是新建一個指向該字符串的指針,即創建一個指針,該指針的值是現有字符串的地址。
構造函數使用new創建一個用於保存指針的數組,析構函數刪除該數組,而不是數組元素指向的字符串。
數組模板示例和非類型模板
常用作容器類,這是因為類型參數的概念非常適合於將相同的存儲方案用於不同的類型。引入模板的主要動機是:容器類可提供重用的代碼。
深入探討模板設計和使用的其他幾個方面。
具體來說,探討一些非類型(或表達式)參數以及如何使用數組。
實現一種允許指定數組大小的簡單數組模板:
方法一:在類中使用動態數組和構造函數參數來提供元素數目;->舊方法
方法二:使用模板參數來提供數組的大小;
模板頭:template<class T, int n>
class 關鍵字標識這是類型參數,int指出n的類型為int,這是非類型參數,或表達式參數;
表達式參數有一些限制:可以是整型,枚舉,引用或指針。
表達式參數的優點:使用自動變量維護內存棧,而不是用構造函數方法使用的new,delete來管理內存。
表達式參數的缺點:下面的聲明將生成兩個獨立的類聲明;
ArrayTP<double, 12> eggweights;
ArrayTP< double, 13> eggweights;
而構造函數的方法,就只要一份類聲明。
另一個區別是:構造函數的方法更通用。數組大小是作為類數據成員存儲在定義中。
模板多功能性
模板的作用:
用作基類
用作組件類
用作其他模板的類型參數
數組模板實現棧模板
數組模板來構造數組
遞歸使用模板
使用多個類型參數
template<class T1, class T2>
默認類型模板參數
可以為類型參數提供默認值;
template<class T1, class T2=int>
類模板:類型參數可提供默認值;
函數模板的類型參數不可聽默認值;
非類型參數:類模板,函數模板都可以提供默認值;
模板的具體化
模板以泛型的方式描述類;
而具體化是使用具體的類型生成類聲明;
1、 隱式實例化
是指在需要對象之前,不會生成類;
ArrayTP<double,30> * pt; //一個指針,還沒有對象被創建
pt = new ArrayTP<double,30>; //創建一個對象
2、 顯式實例化
template class ArrayTP<string,100>; //會產生一個類的實例,聲明一個類;
雖然沒有創建或提及類對象,編譯器也將生成類聲明。
3、 顯式具體化
指的是特定類型的定義。
4、部分具體化
成員模板
模板可用作結構、類或模板類的成員。
類的成員(數據,方法)->都可以用模板表示;
而不僅僅是類用模板表示;
模板之中有模板,嵌套的;還可以在模板之外定義方法模板,成員模板;
將模板用作參數
將模板用作模板的參數;
模板包含類型參數和非類型參數:
template <template <typename T> class Thing>
template <typename T> class Thing 是模板的參數;
其中template <typename T> class 是參數模板(把模板作為參數用);是類型;
假設有以下聲明:
Crab<King> legs;
Crab<King>是模板Crab具體的類型;模板參數是King,King也必須是一個模板類。
template <typename T>
class King {…};
模板類和友元
模板類聲明也可以有友元。(友元:模板or非模板, 約束or非約束)
l 非模板友元;
l 約束模板友元,即友元的類型取決於類被實例化時的類型;
l 非約束模板友元,即友元的所有具體化都是類的每一個具體化的友元;
1、 模板類的非模板友元函數
在模板類中將一個常規函數聲明為友元:
template <class T>
class HasFriend
{
public:
friend void counts();
};
counts()函數成為模板所有實例化的友元。例如,它將是類hasFriend<int>和HasFriend<string>的友元。該友元與所有模板實例化的對象都具有友元關系(一對多)。該函數不是通過對象調用,沒有對象參數。
template <class T>
class HasFriend
{
public:
friend void report(HasFriend<T> &);
};
這一種帶模板類參數的友元。這是一種一對一的友元關系,即該友元只是某個具體類型的模板的友元函數。
2、 模板類的約束模板友元函數
3、 模板類的非約束模板友元函數
普通類+普通友元 (一對一)友元-類而言
普通類+模板友元 (多對一)友元-類而言
模板類+普通友元 (一對多)友元-類而言
模板類+模板友元 (多對多)友元-類而言
模板別名(C++)
C++_代碼重用5-類模板