| 一、類(lèi)模板定義及實(shí)例化1. 定義一個(gè)類(lèi)模板:  
1 template<class 模板參數(shù)表>2
 3 class 類(lèi)名{
 4
 5 // 類(lèi)定義......
 6
 7 };
  其中,template 是聲明類(lèi)模板的關(guān)鍵字,表示聲明一個(gè)模板,模板參數(shù)可以是一個(gè),也可以是多個(gè),可以是類(lèi)型參數(shù) ,也可以是非類(lèi)型參數(shù)。類(lèi)型參數(shù)由關(guān)鍵字class或typename及其后面的標(biāo)識(shí)符構(gòu)成。非類(lèi)型參數(shù)由一個(gè)普通參數(shù)構(gòu)成,代表模板定義中的一個(gè)常量。 例:   
1 template<class type,int width>2
 3 //type為類(lèi)型參數(shù),width為非類(lèi)型參數(shù)
 4
 5 class Graphics;
 注意: (1)如果在全局域中聲明了與模板參數(shù)同名的變量,則該變量被隱藏掉。 (2)模板參數(shù)名不能被當(dāng)作類(lèi)模板定義中類(lèi)成員的名字。 (3)同一個(gè)模板參數(shù)名在模板參數(shù)表中只能出現(xiàn)一次。 (4)在不同的類(lèi)模板或聲明中,模板參數(shù)名可以被重復(fù)使用。   
 1 typedef string type;2
 3 template<class type,int width>
 4
 5 class Graphics
 6
 7 {
 8
 9 type node;//node不是string類(lèi)型
 10
 11 typedef double type;//錯(cuò)誤:成員名不能與模板參數(shù)type同名
 12
 13 };
 14
 15 template<class type,class type>//錯(cuò)誤:重復(fù)使用名為type的參數(shù)
 16
 17 class Rect;
 18
 19 template<class type> //參數(shù)名”type”在不同模板間可以重復(fù)使用
 20
 21 class Round;
(5)在類(lèi)模板的前向聲明和定義中,模板參數(shù)的名字可以不同。
   
 1 // 所有三個(gè) Image 聲明都引用同一個(gè)類(lèi)模板的聲明2
 3 template <class T> class Image;
 4
 5 template <class U> class Image;
 6
 7 // 模板的真正定義
 8
 9 template <class Type>
 10
 11 class Image { //模板定義中只能引用名字”Type”,不能引用名字”T”和”U” };
(6)類(lèi)模板參數(shù)可以有缺省實(shí)參,給參數(shù)提供缺省實(shí)參的順序是先右后左。
  View Code(7)類(lèi)模板名可以被用作一個(gè)類(lèi)型指示符。當(dāng)一個(gè)類(lèi)模板名被用作另一個(gè)模板定義中的類(lèi)型指示符時(shí),必須指定完整的實(shí)參表
  View Code2.類(lèi)模板實(shí)例化
定義:從通用的類(lèi)模板定義中生成類(lèi)的過(guò)程稱(chēng)為模板實(shí)例化。 
 
例:Graphics<int> gi; 類(lèi)模板什么時(shí)候會(huì)被實(shí)例化呢? ①當(dāng)使用了類(lèi)模板實(shí)例的名字,并且上下文環(huán)境要求存在類(lèi)的定義時(shí)。 ②對(duì)象類(lèi)型是一個(gè)類(lèi)模板實(shí)例,當(dāng)對(duì)象被定義時(shí)。此點(diǎn)被稱(chēng)作類(lèi)的實(shí)例化點(diǎn)。 ③一個(gè)指針或引用指向一個(gè)類(lèi)模板實(shí)例,當(dāng)檢查這個(gè)指針或引用所指的對(duì)象時(shí)。 例:   
 1 template<class Type>2
 3 class Graphics{};
 4
 5 void f1(Graphics<char>);// 僅是一個(gè)函數(shù)聲明,不需實(shí)例化
 6
 7 class Rect
 8
 9 {
 10
 11   Graphics<double>& rsd;// 聲明一個(gè)類(lèi)模板引用,不需實(shí)例化
 12
 13   Graphics<int> si;// si是一個(gè)Graphics類(lèi)型的對(duì)象,需要實(shí)例化類(lèi)模板
 14
 15 }
 16
 17 int main(){
 18
 19   Graphcis<char>* sc;// 僅聲明一個(gè)類(lèi)模板指針,不需實(shí)例化
 20
 21   f1(*sc);//需要實(shí)例化,因?yàn)閭鬟f給函數(shù)f1的是一個(gè)Graphics<int>對(duì)象。
 22
 23   int iobj=sizeof(Graphics<string>);//需要實(shí)例化,因?yàn)閟izeof會(huì)計(jì)算Graphics<string>對(duì)象的大小,為了計(jì)算大小,編譯器必須根據(jù)類(lèi)模板定義產(chǎn)生該類(lèi)型。
 24
 25 }
3.非類(lèi)型參數(shù)的模板實(shí)參
要點(diǎn): ①綁定給非類(lèi)型參數(shù)的表達(dá)式必須是一個(gè)常量表達(dá)式。 ②從模板實(shí)參到非類(lèi)型模板參數(shù)的類(lèi)型之間允許進(jìn)行一些轉(zhuǎn)換。包括左值轉(zhuǎn)換、限定修飾轉(zhuǎn)換、提升、整值轉(zhuǎn)換。 ③可以被用于非類(lèi)型模板參數(shù)的模板實(shí)參的種類(lèi)有一些限制。 例:   
 1 Template<int* ptr> class Graphics{…….};2
 3 Template<class Type,int size> class Rect{……..};
 4
 5 const int size=1024;
 6
 7 Graphics<&size> bp1;//錯(cuò)誤:從const int*->int*是錯(cuò)誤的。
 8
 9 Graphics<0> bp2;//錯(cuò)誤不能通過(guò)隱式轉(zhuǎn)換把0轉(zhuǎn)換成指針值
 10
 11 const double db=3.1415;
 12
 13 Rect<double,db> fa1;//錯(cuò)誤:不能將const double轉(zhuǎn)換成int.
 14
 15 unsigned int fasize=255;
 16
 17 Rect<String, fasize> fa2;//錯(cuò)誤:非類(lèi)型參數(shù)的實(shí)參必須是常量表達(dá)式,將unsigned改為const就正確。
 18
 19 Int arr[10];
 20
 21 Graphics<arr> gp;//正確
  二、類(lèi)模板的成員函數(shù)要點(diǎn): ①類(lèi)模板的成員函數(shù)可以在類(lèi)模板的定義中定義(inline函數(shù)),也可以在類(lèi)模板定義之外定義(此時(shí)成員函數(shù)定義前面必須加上template及模板參數(shù))。 ②類(lèi)模板成員函數(shù)本身也是一個(gè)模板,類(lèi)模板被實(shí)例化時(shí)它并不自動(dòng)被實(shí)例化,只有當(dāng)它被調(diào)用或取地址,才被實(shí)例化。   
 1 template<class type>2
 3 Class Graphics{
 4
 5 Graphics(){…}//成員函數(shù)定義在類(lèi)模板的定義中
 6
 7 void out();
 8
 9 };
 10
 11 template<class type>//成員函數(shù)定義在類(lèi)模板定義之外
 12
 13 void Graphics<type>::out(){…}
 三、類(lèi)模板的友元聲明類(lèi)模板中可以有三種友元聲明: 1.非模板友元類(lèi)或友元函數(shù)  
 1 class Graphics{void out();};2
 3 Template<class T>
 4
 5 Class Rect{
 6
 7 friend class Graphics;//類(lèi)Graphics、函數(shù)
 8
 9 friend void create();// create、 out是類(lèi)模板
 10
 11 friend void Graphics::out();// Rect所有實(shí)例的友元
 12
 13 };
 2、綁定的友元類(lèi)模板或函數(shù)模板。3、非綁定的友元類(lèi)模板或函數(shù)模板。第二種聲明表示類(lèi)模板的實(shí)例和它的友元之間是一種一對(duì)一的映射關(guān)系。 如圖: 
 第三種聲明表示類(lèi)模板的實(shí)例和它的友元之間是一種一對(duì)多的映射關(guān)系。 如圖: 
 例:綁定的友元模板   
 1 template<class type>2
 3 void create(Graphics<type>);
 4
 5 template<class type>
 6
 7 class Graphics{
 8
 9 friend void create<type>(Graphics<type>);
 10
 11 };
例:非綁定的友元模板
   
1 template<class type>2
 3 class Graphics{
 4
 5 template<class T>
 6
 7 friend void create(Graphics<T>);
 8
 9 };
注意:當(dāng)把非模板類(lèi)或函數(shù)聲明為類(lèi)模板友元時(shí),它們不必在全局域中被聲明或定義,但將一個(gè)類(lèi)的成員聲明為類(lèi)模板友元,該類(lèi)必須已經(jīng)被定義,另外在聲明綁定的友元類(lèi)模板或函數(shù)模板時(shí),該模板也必須先聲明。
 例:   
 1 template <class T>2
 3 class A {
 4
 5 private:
 6
 7 friend class B<T>; //錯(cuò)誤:類(lèi)B必須先聲明
 8
 9 };
 10
 11 template <class T>
 12
 13 class B{};
 四、類(lèi)模板的靜態(tài)數(shù)據(jù)成員、嵌套類(lèi)型1.類(lèi)模板的靜態(tài)數(shù)據(jù)成員要點(diǎn): ①靜態(tài)數(shù)據(jù)成員的模板定義必須出現(xiàn)在類(lèi)模板定義之外。 ②類(lèi)模板靜態(tài)數(shù)據(jù)成員本身就是一個(gè)模板,它的定義不會(huì)引起內(nèi)存被分配,只有對(duì)其實(shí)例化才會(huì)分配內(nèi)存。 ③當(dāng)程序使用靜態(tài)數(shù)據(jù)成員時(shí),它被實(shí)例化,每個(gè)靜態(tài)成員實(shí)例都與一個(gè)類(lèi)模板實(shí)例相對(duì)應(yīng),靜態(tài)成員的實(shí)例引用要通過(guò)一個(gè)類(lèi)模板實(shí)例。 例:   
 1 template<class type>2
 3 class Graphics{
 4
 5 static Graphics *next;
 6
 7 static const type item;
 8
 9 };
 10
 11 template<class type>
 12
 13 Graphics<type> * Graphics<type>::next=0;
 14
 15 template<class type>
 16
 17 type Graphics<type>::item=NULL;
 18
 19 //靜態(tài)成員定義分為兩部分:前一部分是類(lèi)型,比如Graphics<type>*,后一部分是名稱(chēng)和值,比如Graphics<type>::next=0;
 2.類(lèi)模板的嵌套類(lèi)型要點(diǎn): ①在類(lèi)模板中允許再嵌入模板,因此類(lèi)模板的嵌套類(lèi)也是一個(gè)模板,它可以使用外圍類(lèi)模板的模板參數(shù)。 ②當(dāng)外圍類(lèi)模板被實(shí)例化時(shí),它不會(huì)自動(dòng)被實(shí)例化,只有當(dāng)上下文需要它的完整類(lèi)類(lèi)型時(shí),它才會(huì)被實(shí)例化。 ③公有嵌套類(lèi)型可以被用在類(lèi)定義之外,這時(shí)它的名字前必須加上類(lèi)模板實(shí)例的名字。 例:   
 1 template<class type>2
 3 class Graphics{
 4
 5 public:
 6
 7 template<class T>
 8
 9 class Rect{void out(type a,T b);};
 10
 11 };
 12
 13 Graphics<int>::Rect<double> node;
 14
 15 //引用公有嵌套類(lèi)型必須加上類(lèi)模板實(shí)例名字
五、成員模板
定義:成員定義前加上template及模板參數(shù)表。 要點(diǎn): ①在一個(gè)類(lèi)模板中定義一個(gè)成員模板,意味著該類(lèi)模板的一個(gè)實(shí)例包含了可能無(wú)限多個(gè)嵌套類(lèi)和無(wú)限多個(gè)成員函數(shù). ②只有當(dāng)成員模板被使用時(shí),它才被實(shí)例化. ③成員模板可以定義在其外圍類(lèi)或類(lèi)模板定義之外. 例:   
 1 template<class type>2
 3 class Graphics<type>{
 4
 5 public:template<class T>
 6
 7 class Rect{void out(type a,T b);};};
 8
 9 template<class Gtype> template<class TT>
 10
 11 void Graphics<Gtype>::Rect<TT>::out(Gtype a,TT b){}//成員模板被定義在類(lèi)模板定義之外(要根上完整模板實(shí)參)
 12
 13 Graphics<int>的實(shí)例可能包括下列嵌套類(lèi)型:
 14
 15 Graphics<int>::Rect<double>
 16
 17 Graphics<int>::Rect<string>
注意:類(lèi)模板參數(shù)不一定與類(lèi)模板定義中指定的名字相同。
   六、類(lèi)模板的編譯模式1.包含編譯模式這種編譯模式下,類(lèi)模板的成員函數(shù)和靜態(tài)成員的定義必須被包含在“要將它們實(shí)例化”的所有文件中,如果一個(gè)成員函數(shù)被定義在類(lèi)模板定義之外,那么這些定義應(yīng)該被放在含有該類(lèi)模板定義的頭文件中。 2.分離編譯模式這種模式下,類(lèi)模板定義和其inline成員函數(shù)定義被放在頭文件中,而非inline成員函數(shù)和靜態(tài)數(shù)據(jù)成員被放在程序文本文件中。 例:   
 1 //------Graphics.h---------2
 3 export template<class type>
 4
 5 Class Graphics
 6
 7 {void Setup(const type &);};
 8
 9 //-------Graphics.c------------
 10
 11 #include “Graphics.h”
 12
 13 Template <class type>
 14
 15 Void Graphics<type>::Setup(const type &){…}
 16
 17 //------user.c-----
 18
 19 #include “Graphics.h”
 20
 21 Void main()
 22
 23 {Graphics<int> *pg=new Graphics<int>;
 24
 25 Int ival=1;
 26
 27 //Graphics<int>::Setup(const int &)的實(shí)例(下有注解)
 28
 29 Pg->Setup(ival);
 30
 31 }
Setup的成員定義在User.c中不可見(jiàn),但在這個(gè)文件中仍可調(diào)用模板實(shí)例Graphics<int>::Setup(const int &)。為實(shí)現(xiàn)這一點(diǎn),須將類(lèi)模聲明為可導(dǎo)出的:當(dāng)它的成員函數(shù)實(shí)例或靜態(tài)數(shù)據(jù)成員實(shí)例被使用時(shí),編譯器只要求模板的定義,它的聲明方式是在關(guān)鍵字template前加關(guān)鍵字export
 3.顯式實(shí)例聲明當(dāng)使用包含編譯模式時(shí),類(lèi)模板成員的定義被包含在使用其實(shí)例的所有程序文本文件中,何時(shí)何地編譯器實(shí)例化類(lèi)模板成員的定義,我們并不能精確地知曉,為解決這個(gè)問(wèn)題,標(biāo)準(zhǔn)C++提供了顯式實(shí)例聲明:關(guān)鍵字template后面跟著關(guān)鍵字class以及類(lèi)模板實(shí)例的名字。 例:   
1 #include “Graphics.h”2
 3 Template class Graphics<int>;//顯式實(shí)例聲明
顯式實(shí)例化類(lèi)模板時(shí),它的所有成員也被顯式實(shí)例化。
   七、類(lèi)模板的特化及部分特化1.類(lèi)模板的特化先看下面的例子:   
1 Template<class type>2
 3 Class Graphics{
 4
 5 Public:void out(type figure){…}};
 6
 7 Class Rect{…};
如果模板實(shí)參是Rect類(lèi)型,我們不希望使用類(lèi)模板Graphics的通用成員函數(shù)定義,來(lái)實(shí)例化成員函數(shù)out(),我們希望專(zhuān)門(mén)定義Graphics<Rect>::out()實(shí)例,讓它使用Rect里面的成員函數(shù)。
 為此,我們可以通過(guò)一個(gè)顯示特化定義,為類(lèi)模板實(shí)例的一個(gè)成員提供一個(gè)特化定義。 格式:template<> 成員函數(shù)特化定義 下面為類(lèi)模板實(shí)例Graphics<Rect>的成員函數(shù)out()定義了顯式特化: 
Template<> void Graphics<Rect>::out(Rect figure){…} 注意: ①只有當(dāng)通用類(lèi)模板被聲明后,它的顯式特化才可以被定義。 ②若定義了一個(gè)類(lèi)模板特化,則必須定義與這個(gè)特化相關(guān)的所有成員函數(shù)或靜態(tài)數(shù)據(jù)成員,此時(shí)類(lèi)模板特化的成員定義不能以符號(hào)template<>作為打頭。(template<>被省略) ③類(lèi)模板不能夠在某些文件中根據(jù)通用模板定義被實(shí)例化,而在其他文件中卻針對(duì)同一組模板實(shí)參被特化。 2.類(lèi)模板部分特化如果模板有一個(gè)以上的模板參數(shù),則有些人就可能希望為一個(gè)特定的模板實(shí)參或者一組模板實(shí)參特化類(lèi)模板,而不是為所有的模板參數(shù)特化該類(lèi)模板。即,希望提供這樣一個(gè)模板:它仍然是一個(gè)通用的模板,只不過(guò)某些模板參數(shù)已經(jīng)被實(shí)際的類(lèi)型或值取代。通過(guò)使用類(lèi)模板部分特化,可以實(shí)現(xiàn)這一點(diǎn)。 例:   
1 template<int hi,int wid>2
 3 Class Graphics{…};
 4
 5 Template<int hi>//類(lèi)模板的部分特化
 6
 7 Class Graphics<hi,90>{…};
格式:template<模板參數(shù)表>
 注意: ①部分特化的模板參數(shù)表只列出模板實(shí)參仍然未知的那些參數(shù)。 ②類(lèi)模板部分特化是被隱式實(shí)例化的。編譯器選擇“針對(duì)該實(shí)例而言最為特化的模板定義”進(jìn)行實(shí)例化,當(dāng)沒(méi)有特化可被使用時(shí),才使用通用模板定義。 
例:Graphics<24,90> figure; 它即能從通用類(lèi)模板定義被實(shí)例化,也能從部分特化的定義被實(shí)例化,但編譯器選擇的是部分特化來(lái)實(shí)例化模板。 ③類(lèi)模板部分特化必須有它自己對(duì)成員函數(shù)、靜態(tài)數(shù)據(jù)成員和嵌套類(lèi)的定義。   八、名字空間和類(lèi)模板類(lèi)模板定義也可以被放在名字空間中。例如:   
 1 Namespace cplusplus_primer{2
 3 Template<class type>
 4
 5 Class Graphics{…};
 6
 7 Template<class type>
 8
 9 Type create()
 10
 11 {…}
 12
 13 }
當(dāng)類(lèi)模板名字Graphics被用在名字空間之外時(shí),它必須被名字空間名cplusplus_primer限定修,或者通過(guò)一個(gè)using聲明或指示符被引入。例如:
   
1 Void main()2
 3 {
 4
 5 using cplusplus_primer::Graphics;
 6
 7 Graphics<int> *pg=new Graphics<int>;
 8
 9 }
 注意:在名字空間中聲明類(lèi)模板也會(huì)影響該類(lèi)模板及其成員的特化和部分特化聲明的方式,類(lèi)模板或類(lèi)模板成員的特化聲明必須被聲明在定義通用模板的名字空間中(可以在名字空間之外定義模板特化)。 一個(gè)關(guān)于隊(duì)列的例子,下面將其代碼整理如下:     1 #include "iostream.h"2
 3 template <class Type> class QueueItem;
 4
 5 template <class Type>
 6
 7 class Queue {
 8
 9 public:
 10
 11 friend ostream& operator<<(ostream &os,const Queue<Type> &q);
 12
 13 Queue() : front( 0 ), back ( 0 ) { }
 14
 15 ~Queue(){}
 16
 17 void add( const Type & );
 18
 19 bool is_empty() const
 20
 21 {
 22
 23 return front == 0;
 24
 25 }
 26
 27 Type remove();
 28
 29 private:
 30
 31 QueueItem<Type> *front;
 32
 33 QueueItem<Type> *back;
 34
 35 };
 36
 37 template <class Type>
 38
 39 class QueueItem
 40
 41 {
 42
 43 public:
 44
 45 QueueItem(Type val){item=val;next=0;}
 46
 47 friend class Queue<Type>;
 48
 49 friend ostream& operator<<(ostream &os,const Queue<Type> &q);
 50
 51 friend ostream& operator<<(ostream &os,const QueueItem<Type> &qi);
 52
 53
 54
 55 private:
 56
 57 Type item;
 58
 59 QueueItem *next;
 60
 61 };
 62
 63 template <class Type>
 64
 65 void Queue<Type>::add(const Type &val)
 66
 67 {
 68
 69 QueueItem<Type> *pt =new QueueItem<Type>(val);
 70
 71 if ( is_empty() )
 72
 73 front = back = pt;
 74
 75 else
 76
 77 {
 78
 79 back->next = pt;
 80
 81 back = pt;
 82
 83 }
 84
 85 }
 86
 87 template <class Type>
 88
 89 Type Queue<Type>::remove()
 90
 91 {
 92
 93 if ( is_empty() )
 94
 95 {
 96
 97 cerr << "remove() on empty queue \n";
 98
 99 exit(-1);
 100
 101 }
 102
 103 QueueItem<Type> *pt = front;
 104
 105 front = front->next;
 106
 107 Type retval = pt->item;
 108
 109 delete pt;
 110
 111 return retval;
 112
 113 }
 114
 115 template <class Type>
 116
 117 ostream& operator<<(ostream &os, const Queue<Type> &q) //輸出隊(duì)列成員
 118
 119 {
 120
 121 os << "< ";
 122
 123 QueueItem<Type> *p;
 124
 125 for ( p = q.front; p; p = p->next )
 126
 127 os << *p << “ ;//用到了Queue和QueueItem的私有成員,因此需將此運(yùn)算符重
 128
 129 //載函數(shù)聲明為Queue和QueueItem的友元,書(shū)上沒(méi)有將此函數(shù)聲明為QueueItem
 130
 131 os << “ >”;//的友元。
 132
 133 return os;
 134
 135 }
 136
 137 template <class Type>
 138
 139 ostream& operator<< ( ostream &os, const QueueItem<Type> &qi )
 140
 141 {
 142
 143 os << qi.item;//用到了QueueItem的私有成員,因此需將此運(yùn)算符重載函數(shù)聲明
 144
 145 //為QueueItem的友元
 146
 147 return os;
 148
 149 }
 150
 151 void main()
 152
 153 {
 154
 155 Queue<int> qi;
 156
 157 cout << qi << endl;
 158
 159 int ival;
 160
 161 for ( ival = 0; ival < 10; ++ival )
 162
 163 qi.add( ival );
 164
 165 cout << qi << endl;
 166
 167 int err_cnt = 0;
 168
 169 for ( ival = 0; ival < 10; ++ival ) {
 170
 171 int qval = qi.remove();
 172
 173 if ( ival != qval ) err_cnt++;
 174
 175 }
 176
 177 cout << qi << endl;
 178
 179 if ( !err_cnt )
 180
 181 cout << "!! queue executed ok\n";
 182
 183 else cout << “?? queue errors: " << err_cnt << endl;
 184
 185 }
 運(yùn)行結(jié)果
 |