templateclass 類名{ // 類定義...... };
其中,template 是聲明類模板的關鍵字,表示聲明一個模板,模板參數可以是一個,也可以是多個,可以是類型參數 ,也可以是非類型參數。類型參數由關鍵字class或typename及其後面的標識符構成。非類型參數由一個普通參數構成,代表模板定義中的一個常量。
例:
template//type為類型參數,width為非類型參數 class Graphics;
注意:
(1)如果在全局域中聲明了與模板參數同名的變量,則該變量被隱藏掉。
(2)模板參數名不能被當作類模板定義中類成員的名字。
(3)同一個模板參數名在模板參數表中只能出現一次。
(4)在不同的類模板或聲明中,模板參數名可以被重復使用。
typedef string type;
2 templateclass Graphics { type node;//node不是string類型 typedef double type;//錯誤:成員名不能與模板參數type同名 }; template //錯誤:重復使用名為type的參數 class Rect; template //參數名”type”在不同模板間可以重復使用 class Round;
(5)在類模板的前向聲明和定義中,模板參數的名字可以不同。
// 所有三個 Image 聲明都引用同一個類模板的聲明 templateclass Image; template class Image; // 模板的真正定義 template class Image { //模板定義中只能引用名字”Type”,不能引用名字”T”和”U” };
(6)類模板參數可以有缺省實參,給參數提供缺省實參的順序是先右後左。
templateclass Image; template class Image;
(7)類模板名可以被用作一個類型指示符。當一個類模板名被用作另一個模板定義中的類型指示符時,必須指定完整的實參表
templateclass Graphics { Graphics *next;//在類模板自己的定義中不需指定完整模板參數表 }; template void show(Graphics &g) { Graphics *pg=&g;//必須指定完整的模板參數表 }
定義:從通用的類模板定義中生成類的過程稱為模板實例化。
例:Graphics
gi;
類模板什麼時候會被實例化呢?
①當使用了類模板實例的名字,並且上下文環境要求存在類的定義時。
②對象類型是一個類模板實例,當對象被定義時。此點被稱作類的實例化點。
③一個指針或引用指向一個類模板實例,當檢查這個指針或引用所指的對象時。
例:
templateclass Graphics{}; void f1(Graphics );// 僅是一個函數聲明,不需實例化 class Rect { Graphics & rsd;// 聲明一個類模板引用,不需實例化 Graphics si;// si是一個Graphics類型的對象,需要實例化類模板 } int main(){ Graphcis * sc;// 僅聲明一個類模板指針,不需實例化 f1(*sc);//需要實例化,因為傳遞給函數f1的是一個Graphics 對象。 int iobj=sizeof(Graphics );//需要實例化,因為sizeof會計算Graphics 對象的大小,為了計算大小,編譯器必須根據類模板定義產生該類型。 }
要點:
①綁定給非類型參數的表達式必須是一個常量表達式。
②從模板實參到非類型模板參數的類型之間允許進行一些轉換。包括左值轉換、限定修飾轉換、提升、整值轉換。
③可以被用於非類型模板參數的模板實參的種類有一些限制。
例:
Templateclass Graphics{…….}; Template class Rect{……..}; const int size=1024; Graphics<&size> bp1;//錯誤:從const int*->int*是錯誤的。 Graphics<0> bp2;//錯誤不能通過隱式轉換把0轉換成指針值 const double db=3.1415; Rect fa1;//錯誤:不能將const double轉換成int. unsigned int fasize=255; Rect fa2;//錯誤:非類型參數的實參必須是常量表達式,將unsigned改為const就正確。 Int arr[10]; Graphics gp;//正確
要點:
①類模板的成員函數可以在類模板的定義中定義(inline函數),也可以在類模板定義之外定義(此時成員函數定義前面必須加上template及模板參數)。
②類模板成員函數本身也是一個模板,類模板被實例化時它並不自動被實例化,只有當它被調用或取地址,才被實例化。
templateClass Graphics{ Graphics(){…}//成員函數定義在類模板的定義中 void out(); }; template //成員函數定義在類模板定義之外 void Graphics ::out(){…}
類模板中可以有三種友元聲明:
class Graphics{void out();}; TemplateClass Rect{ friend class Graphics;//類Graphics、函數 friend void create();// create、 out是類模板 friend void Graphics::out();// Rect所有實例的友元 };
第二種聲明表示類模板的實例和它的友元之間是一種一對一的映射關系。
如圖:
第三種聲明表示類模板的實例和它的友元之間是一種一對多的映射關系。
如圖:
例:綁定的友元模板
templatevoid create(Graphics ); template class Graphics{ friend void create (Graphics ); };
例:非綁定的友元模板
templateclass Graphics{ template friend void create(Graphics ); };
注意:當把非模板類或函數聲明為類模板友元時,它們不必在全局域中被聲明或定義,但將一個類的成員聲明為類模板友元,該類必須已經被定義,另外在聲明綁定的友元類模板或函數模板時,該模板也必須先聲明。
例:
templateclass A { private: friend class B ; //錯誤:類B必須先聲明 }; template class B{};
要點:
①靜態數據成員的模板定義必須出現在類模板定義之外。
②類模板靜態數據成員本身就是一個模板,它的定義不會引起內存被分配,只有對其實例化才會分配內存。
③當程序使用靜態數據成員時,它被實例化,每個靜態成員實例都與一個類模板實例相對應,靜態成員的實例引用要通過一個類模板實例。
例:
templateclass Graphics{ static Graphics *next; static const type item; }; template Graphics * Graphics ::next=0; template type Graphics ::item=NULL; //靜態成員定義分為兩部分:前一部分是類型,比如Graphics *,後一部分是名稱和值,比如Graphics ::next=0;
要點:
①在類模板中允許再嵌入模板,因此類模板的嵌套類也是一個模板,它可以使用外圍類模板的模板參數。
②當外圍類模板被實例化時,它不會自動被實例化,只有當上下文需要它的完整類類型時,它才會被實例化。
③公有嵌套類型可以被用在類定義之外,這時它的名字前必須加上類模板實例的名字。
例:
templateclass Graphics{ public: template class Rect{void out(type a,T b);}; }; Graphics ::Rect node; //引用公有嵌套類型必須加上類模板實例名字
定義:成員定義前加上template及模板參數表。
要點:
①在一個類模板中定義一個成員模板,意味著該類模板的一個實例包含了可能無限多個嵌套類和無限多個成員函數.
②只有當成員模板被使用時,它才被實例化.
③成員模板可以定義在其外圍類或類模板定義之外.
例:
templateclass Graphics { public:template class Rect{void out(type a,T b);};}; template template void Graphics ::Rect::out(Gtype a,TT b){}//成員模板被定義在類模板定義之外(要根上完整模板實參) Graphics 的實例可能包括下列嵌套類型: Graphics ::Rect Graphics ::Rect
注意:類模板參數不一定與類模板定義中指定的名字相同。
這種編譯模式下,類模板的成員函數和靜態成員的定義必須被包含在“要將它們實例化”的所有文件中,如果一個成員函數被定義在類模板定義之外,那麼這些定義應該被放在含有該類模板定義的頭文件中。
這種模式下,類模板定義和其inline成員函數定義被放在頭文件中,而非inline成員函數和靜態數據成員被放在程序文本文件中。
例:
//------Graphics.h--------- export templateClass Graphics {void Setup(const type &);}; //-------Graphics.c------------ #include “Graphics.h” Template Void Graphics ::Setup(const type &){…} //------user.c----- #include “Graphics.h” Void main() {Graphics *pg=new Graphics ; Int ival=1; //Graphics ::Setup(const int &)的實例(下有注解) Pg->Setup(ival); }
Setup的成員定義在User.c中不可見,但在這個文件中仍可調用模板實例Graphics
當使用包含編譯模式時,類模板成員的定義被包含在使用其實例的所有程序文本文件中,何時何地編譯器實例化類模板成員的定義,我們並不能精確地知曉,為解決這個問題,標准C++提供了顯式實例聲明:關鍵字template後面跟著關鍵字class以及類模板實例的名字。
例:
#include “Graphics.h” Template class Graphics;//顯式實例聲明
顯式實例化類模板時,它的所有成員也被顯式實例化。
先看下面的例子:
TemplateClass Graphics{ Public:void out(type figure){…}}; Class Rect{…};
如果模板實參是Rect類型,我們不希望使用類模板Graphics的通用成員函數定義,來實例化成員函數out(),我們希望專門定義Graphics
為此,我們可以通過一個顯示特化定義,為類模板實例的一個成員提供一個特化定義。
格式:template<> 成員函數特化定義
下面為類模板實例Graphics
Template<> void Graphics
::out(Rect figure){…}
注意:
①只有當通用類模板被聲明後,它的顯式特化才可以被定義。
②若定義了一個類模板特化,則必須定義與這個特化相關的所有成員函數或靜態數據成員,此時類模板特化的成員定義不能以符號template<>作為打頭。(template<>被省略)
③類模板不能夠在某些文件中根據通用模板定義被實例化,而在其他文件中卻針對同一組模板實參被特化。
如果模板有一個以上的模板參數,則有些人就可能希望為一個特定的模板實參或者一組模板實參特化類模板,而不是為所有的模板參數特化該類模板。即,希望提供這樣一個模板:它仍然是一個通用的模板,只不過某些模板參數已經被實際的類型或值取代。通過使用類模板部分特化,可以實現這一點。
例:
templateClass Graphics{…}; Template //類模板的部分特化 Class Graphics {…};
格式:template<模板參數表>
注意:
①部分特化的模板參數表只列出模板實參仍然未知的那些參數。
②類模板部分特化是被隱式實例化的。編譯器選擇“針對該實例而言最為特化的模板定義”進行實例化,當沒有特化可被使用時,才使用通用模板定義。
例:Graphics<24,90> figure;
它即能從通用類模板定義被實例化,也能從部分特化的定義被實例化,但編譯器選擇的是部分特化來實例化模板。
③類模板部分特化必須有它自己對成員函數、靜態數據成員和嵌套類的定義。
類模板定義也可以被放在名字空間中。例如:
Namespace cplusplus_primer{ TemplateClass Graphics{…}; Template Type create() {…} }
當類模板名字Graphics被用在名字空間之外時,它必須被名字空間名cplusplus_primer限定修,或者通過一個using聲明或指示符被引入。例如:
Void main() { using cplusplus_primer::Graphics; Graphics*pg=new Graphics ; }
注意:在名字空間中聲明類模板也會影響該類模板及其成員的特化和部分特化聲明的方式,類模板或類模板成員的特化聲明必須被聲明在定義通用模板的名字空間中(可以在名字空間之外定義模板特化)。
一個關於棧的例子,下面將其代碼整理如下:
#include#include #include #include #include using namespace std; template class Stack { private: vector elems; // 元素 public: void push(T const&); // 入棧 void pop(); // 出棧 T top() const; // 返回棧頂元素 bool empty() const{ // 如果為空則返回真。 return elems.empty(); } }; template void Stack ::push (T const& elem) { // 追加傳入元素的副本 elems.push_back(elem); } template void Stack ::pop () { if (elems.empty()) { throw out_of_range("Stack<>::pop(): empty stack"); } // 刪除最後一個元素 elems.pop_back(); } template T Stack ::top () const { if (elems.empty()) { throw out_of_range("Stack<>::top(): empty stack"); } // 返回最後一個元素的副本 return elems.back(); } int main() { try { Stack intStack; // int 類型的棧 Stack stringStack; // string 類型的棧 // 操作 int 類型的棧 intStack.push(7); cout << intStack.top() < 函數模板例子: /*函數模板*/ #includeusing namespace std; template T getmax(T a,T b) { return (a>b)?a:b; } int main() { int i=5,j=7,k; double a=2.5,b=1.5,c; k=getmax(i,j); c=getmax(a,b); cout< 類模板例子: #includeusing namespace std; template class val { T value1,value2; public: val(T first,T second) { value1=first; value2=second; } T getmax() { T result; result=(value1>value2)?value1:value2; return (result); } }; int main() { val obj(50,70); cout<<"max="<