1. 程式人生 > >C++中的類模板詳細講述

C++中的類模板詳細講述

技術 要求 ebe div spa 限制 dcb 模板的模板 文本文件

一、類模板定義及實例化

. 定義一個類模板:

技術分享 技術分享
1 template<class 模板參數表>
2
3 class 類名{
4
5 // 類定義......
6
7 };
技術分享

其中,template 是聲明類模板的關鍵字,表示聲明一個模板,模板參數可以是一個,也可以是多個,可以是類型參數 ,也可以是非類型參數。類型參數由關鍵字class或typename及其後面的標識符構成。非類型參數由一個普通參數構成,代表模板定義中的一個常量。

例:

技術分享
1 template<class type,int width>
2
3 //type為類型參數,width為非類型參數
4
5 class Graphics;

註意:

(1)如果在全局域中聲明了與模板參數同名的變量,則該變量被隱藏掉。

(2)模板參數名不能被當作類模板定義中類成員的名字。

(3)同一個模板參數名在模板參數表中只能出現一次。

(4)在不同的類模板或聲明中,模板參數名可以被重復使用。

技術分享 技術分享
 1 typedef string type;
2
3 template<class type,int width>
4
5 class Graphics
6
7 {
8
9 type node;//node不是string類型
10
11 typedef double type;//錯誤:成員名不能與模板參數type同名
12
13 };
14
15 template<class type,class type>//錯誤:重復使用名為type的參數
16
17 class Rect;
18
19 template<class type> //參數名”type”在不同模板間可以重復使用
20
21 class Round;
技術分享


(5)
在類模板的前向聲明和定義中,模板參數的名字可以不同。

技術分享 技術分享
 1 // 所有三個 Image 聲明都引用同一個類模板的聲明
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)
類模板參數可以有缺省實參,給參數提供缺省實參的順序是先右後左。

技術分享 技術分享
1 template <class type, int size = 1024>
2
3 class Image;
4
5 template <class type=double, int size >
6
7 class Image;
技術分享


(7)
類模板名可以被用作一個類型指示符。當一個類模板名被用作另一個模板定義中的類型指示符時,必須指定完整的實參表

技術分享 技術分享
 1 template<class type>
2
3 class Graphics
4
5 {
6
7 Graphics *next;//在類模板自己的定義中不需指定完整模板參數表
8
9 };
10
11 template <calss type>
12
13 void show(Graphics<type> &g)
14
15 {
16
17 Graphics<type> *pg=&g;//必須指定完整的模板參數表
18
19 }
技術分享


2.
類模板實例化

定義:從通用的類模板定義中生成類的過程稱為模板實例化。

技術分享

例:Graphics<int> gi;

類模板什麽時候會被實例化呢?

當使用了類模板實例的名字,並且上下文環境要求存在類的定義時。

對象類型是一個類模板實例,當對象被定義時。此點被稱作類的實例化點

一個指針或引用指向一個類模板實例,當檢查這個指針或引用所指的對象時。

例:

技術分享 技術分享
 1 template<class Type>
2
3 class Graphics{};
4
5 void f1(Graphics<char>);// 僅是一個函數聲明,不需實例化
6
7 class Rect
8
9 {
10
11   Graphics<double>& rsd;// 聲明一個類模板引用,不需實例化
12
13   Graphics<int> si;// si是一個Graphics類型的對象,需要實例化類模板
14
15 }
16
17 int main(){
18
19   Graphcis<char>* sc;// 僅聲明一個類模板指針,不需實例化
20
21   f1(*sc);//需要實例化,因為傳遞給函數f1的是一個Graphics<int>對象。
22
23   int iobj=sizeof(Graphics<string>);//需要實例化,因為sizeof會計算Graphics<string>對象的大小,為了計算大小,編譯器必須根據類模板定義產生該類型。
24
25 }
技術分享


3.
非類型參數的模板實參

要點

綁定給非類型參數的表達式必須是一個常量表達式。

從模板實參到非類型模板參數的類型之間允許進行一些轉換。包括左值轉換、限定修飾轉換、提升、整值轉換。

可以被用於非類型模板參數的模板實參的種類有一些限制。

例:

技術分享View Code

二、類模板的成員函數

要點:

類模板的成員函數可以在類模板的定義中定義(inline函數),也可以在類模板定義之外定義(此時成員函數定義前面必須加上template及模板參數)。

類模板成員函數本身也是一個模板,類模板被實例化時它並不自動被實例化,只有當它被調用或取地址,才被實例化。

技術分享View Code

三、類模板的友元聲明

類模板中可以有三種友元聲明:

.非模板友元類或友元函數

技術分享View Code

2、綁定的友元類模板或函數模板。

3、非綁定的友元類模板或函數模板。

第二種聲明表示類模板的實例和它的友元之間是一種一對一的映射關系。

如圖:

技術分享

第三種聲明表示類模板的實例和它的友元之間是一種一對多的映射關系。

如圖:

技術分享

例:綁定的友元模板

技術分享View Code


例:非綁定的友元模板

技術分享View Code


註意
當把非模板類或函數聲明為類模板友元時,它們不必在全局域中被聲明或定義,但將一個類的成員聲明為類模板友元,該類必須已經被定義,另外在聲明綁定的友元類模板或函數模板時,該模板也必須先聲明。

例:

技術分享View Code

四、類模板的靜態數據成員、嵌套類型

.類模板的靜態數據成員

要點:

靜態數據成員的模板定義必須出現在類模板定義之外。

類模板靜態數據成員本身就是一個模板,它的定義不會引起內存被分配,只有對其實例化才會分配內存。

當程序使用靜態數據成員時,它被實例化,每個靜態成員實例都與一個類模板實例相對應,靜態成員的實例引用要通過一個類模板實例。

例:

技術分享View Code

2.類模板的嵌套類型

要點

在類模板中允許再嵌入模板,因此類模板的嵌套類也是一個模板,它可以使用外圍類模板的模板參數。

當外圍類模板被實例化時,它不會自動被實例化,只有當上下文需要它的完整類類型時,它才會被實例化。

公有嵌套類型可以被用在類定義之外,這時它的名字前必須加上類模板實例的名字。

例:

技術分享View Code


五、成員模板

定義:成員定義前加上template及模板參數表。

要點:

在一個類模板中定義一個成員模板,意味著該類模板的一個實例包含了可能無限多個嵌套類和無限多個成員函數.

只有當成員模板被使用時,它才被實例化.

成員模板可以定義在其外圍類或類模板定義之外.

例:

技術分享View Code


註意:類模板參數不一定與類模板定義中指定的名字相同。

六、類模板的編譯模式

1.包含編譯模式

這種編譯模式下,類模板的成員函數和靜態成員的定義必須被包含在“要將它們實例化”的所有文件中,如果一個成員函數被定義在類模板定義之外,那麽這些定義應該被放在含有該類模板定義的頭文件中。

2.分離編譯模式

這種模式下,類模板定義和其inline成員函數定義被放在頭文件中,而非inline成員函數和靜態數據成員被放在程序文本文件中。

例:

技術分享View Code


Setup的成員定義在User.c中不可見,但在這個文件中仍可調用模板實例Graphics<int>::Setup(const int &)。為實現這一點,須將類模聲明為可導出的:當它的成員函數實例或靜態數據成員實例被使用時,編譯器只要求模板的定義,它的聲明方式是在關鍵字template前加關鍵字export

.顯式實例聲明

當使用包含編譯模式時,類模板成員的定義被包含在使用其實例的所有程序文本文件中,何時何地編譯器實例化類模板成員的定義,我們並不能精確地知曉,為解決這個問題,標準C++提供了顯式實例聲明:關鍵字template後面跟著關鍵字class以及類模板實例的名字。

例:

技術分享View Code


顯式實例化類模板時,它的所有成員也被顯式實例化。

七、類模板的特化及部分特化

1.類模板的特化

先看下面的例子:

技術分享View Code


如果模板實參是Rect類型,我們不希望使用類模板Graphics的通用成員函數定義,來實例化成員函數out(),我們希望專門定義Graphics<Rect>::out()實例,讓它使用Rect裏面的成員函數。

為此,我們可以通過一個顯示特化定義,為類模板實例的一個成員提供一個特化定義。

格式:template<> 成員函數特化定義

下面為類模板實例Graphics<Rect>的成員函數out()定義了顯式特化:

Template<> void Graphics<Rect>::out(Rect figure){…}

註意:

只有當通用類模板被聲明後,它的顯式特化才可以被定義。

若定義了一個類模板特化,則必須定義與這個特化相關的所有成員函數或靜態數據成員,此時類模板特化的成員定義不能以符號template<>作為打頭。(template<>被省略)

類模板不能夠在某些文件中根據通用模板定義被實例化,而在其他文件中卻針對同一組模板實參被特化。

2.類模板部分特化

如果模板有一個以上的模板參數,則有些人就可能希望為一個特定的模板實參或者一組模板實參特化類模板,而不是為所有的模板參數特化該類模板。即,希望提供這樣一個模板:它仍然是一個通用的模板,只不過某些模板參數已經被實際的類型或值取代。通過使用類模板部分特化,可以實現這一點。

例:

技術分享View Code


格式:
template<模板參數表>

註意:

部分特化的模板參數表只列出模板實參仍然未知的那些參數。

類模板部分特化是被隱式實例化的。編譯器選擇“針對該實例而言最為特化的模板定義”進行實例化,當沒有特化可被使用時,才使用通用模板定義。

例:Graphics<24,90> figure;

它即能從通用類模板定義被實例化,也能從部分特化的定義被實例化,但編譯器選擇的是部分特化來實例化模板。

類模板部分特化必須有它自己對成員函數、靜態數據成員和嵌套類的定義。

八、名字空間和類模板

類模板定義也可以被放在名字空間中。例如:

技術分享View Code


當類模板名字Graphics被用在名字空間之外時,它必須被名字空間名cplusplus_primer限定修,或者通過一個using聲明或指示符被引入。例如:

技術分享View Code

註意:在名字空間中聲明類模板也會影響該類模板及其成員的特化和部分特化聲明的方式,類模板或類模板成員的特化聲明必須被聲明在定義通用模板的名字空間中(可以在名字空間之外定義模板特化)。

一個關於隊列的例子,下面將其代碼整理如下:

技術分享View Code


運行結果

C++中的類模板詳細講述