C++ 模板簡介

C++ 模板簡介

一、模板

使用模板的目的就是能夠讓程序員編寫與類型無關(guān)的代碼。

模板是一種對類型進行參數(shù)化的工具,通常有兩種形式:函數(shù)模板類模板。函數(shù)模板針對僅參數(shù)類型不同的函數(shù);類模板針對僅數(shù)據(jù)成員和成員函數(shù)類型不同的類。

注意:
模板的聲明或定義只能在全局,命名空間或類范圍內(nèi)進行。即不能在局部范圍,函數(shù)內(nèi)進行,比如不能在main函數(shù)中聲明或定義一個模板。

二、函數(shù)模板

函數(shù)模板定義了參數(shù)化的非成員函數(shù),這使得程序員能夠用不同類型的參數(shù)調(diào)用相同的函數(shù),由編譯器決定調(diào)用哪一種類型,并且從模板中生成相應(yīng)的代碼。

定義一個函數(shù)模板:

template <class 形參名, class 形參名, ...> 
返回類型 函數(shù)名(參數(shù)列表)
{ ... }

其中 template 和 class 是關(guān)鍵字,class 可以用 typename 關(guān)鍵字代替,在這里 typename 和 class 沒區(qū)別,<>括號中的參數(shù)叫模板形參,模板形參和函數(shù)形參很相像,模板形參不能為空。一但聲明了模板函數(shù)就可以用模板函數(shù)的形參名聲明類中的成員變量和成員函數(shù),即可以在該函數(shù)中使用內(nèi)置類型的地方都可以使用模板形參名。模板形參需要調(diào)用該模板函數(shù)時提供的模板實參來初始化模板形參,一旦編譯器確定了實際的模板實參類型就稱他實例化了函數(shù)模板的一個實例。

三、類模板

1. 類模板定義及實例化

定義一個類模板:

template <class 形參名, class 形參名, ...>   
class 類名
{ ... };

其中,template 是聲明類模板的關(guān)鍵字,表示聲明一個模板,模板參數(shù)可以是一個,也可以是多個,可以是類型參數(shù) ,也可以是非類型參數(shù)。類型參數(shù)由關(guān)鍵字 class 或 typename 及其后面的標識符構(gòu)成。非類型參數(shù)由一個普通參數(shù)構(gòu)成,代表模板定義中的一個常量。
注意:
(1) 如果在全局域中聲明了與模板參數(shù)同名的變量,則該變量被隱藏掉。
(2) 模板參數(shù)名不能被當作類模板定義中類成員的名字。
(3) 同一個模板參數(shù)名在模板參數(shù)表中只能出現(xiàn)一次。
(4) 在不同的類模板或聲明中,模板參數(shù)名可以被重復(fù)使用。
(5) 在類模板的前向聲明和定義中,模板參數(shù)的名字可以不同。
(6) 類模板參數(shù)可以有缺省實參,給參數(shù)提供缺省實參的順序是先右后左。
(7) 類模板名可以被用作一個類型指示符。當一個類模板名被用作另一個模板定義中的類型指示符時,必須指定完整的實參表

類型參數(shù)和非類型參數(shù)

  • 1. 類型參數(shù):類型參數(shù)由關(guān)鍵字 class 或 typename 后接說明符構(gòu)成,如 template <class T> void h(T a){};其中 T 就是一個類型參數(shù),類型參數(shù)的名字由用戶自已確定。類型參數(shù)表示的是一個未知的類型。類型參數(shù)可作為類型說明符用在模板中的任何地方,與內(nèi)置類型說明符或類類型說明符的使用方式完全相同,即可以用于指定返回類型,變量聲明等。

    • 1.1 針對函數(shù)模板,不能為同一個模板類型形參指定兩種不同的類型,比如template <class T>void h(T a, T b){},語句調(diào)用 h(2, 3.2) 將出錯,因為該語句給同一模板形參 T 指定了兩種類型,第一個實參 2 把模板形參 T 指定為 int,而第二個實參 3.2 把模板形參指定為 double,兩種類型的形參不一致,會出錯。
    • 1.2 針對類模板,當我們聲明類對象為:A<int> a,比如 template <class T>T g(T a, T b){},語句調(diào)用 a.g(2, 3.2) 在編譯時不會出錯,但會有警告,因為在聲明類對象的時候已經(jīng)將T轉(zhuǎn)換為 int 類型,而第二個實參 3.2 把模板形參指定為 double,在運行時,會對 3.2 進行強制類型轉(zhuǎn)換為 3 。當我們聲明類的對象為:A<double> a ,此時就不會有上述的警告,因為從 int 到 double 是自動類型轉(zhuǎn)換。
  • 2. 非類型參數(shù):模板的非類型形參也就是內(nèi)置類型形參,如template<class T, int a> class B{};其中int a就是非類型的模板形參。

    • **2.1 **非類型模板的形參只能是整型,指針和引用。
    • **2.2 **調(diào)用非類型模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結(jié)果。
    • **2.3 **任何局部對象,局部變量,局部對象的地址,局部變量的地址都不是一個常量表達式,都不能用作非類型模板形參的實參。全局指針類型,全局變量,全局對象也不是一個常量表達式,不能用作非類型模板形參的實參。
    • **2.4 **全局變量的地址或引用,全局對象的地址或引用const類型變量是常量表達式,可以用作非類型模板形參的實參。
    • **2.5 **sizeof表達式的結(jié)果是一個常量表達式,也能用作非類型模板形參的實參。
    • **2.6 **非類型形參一般不應(yīng)用于函數(shù)模板中,比如有函數(shù)模板template<class T, int a> void h(T b){},若使用 h(2) 調(diào)用會出現(xiàn)無法為非類型形參 a 推演出參數(shù)的錯誤,對這種模板函數(shù)可以用顯示模板實參來解決,如用 h<int, 3>(2) 這樣就把非類型形參 a 設(shè)置為整數(shù) 3。
    • **2.7 **非類型模板形參的形參和實參間所允許的轉(zhuǎn)換:

1、允許從數(shù)組到指針,從函數(shù)到指針的轉(zhuǎn)換。如:template <int *a> class A{}; int b[1]; A <b> m; 即數(shù)組到指針的轉(zhuǎn)換
2、const 修飾符的轉(zhuǎn)換。如:template<const int *a> class A{}; int b; A<&b> m; 即從int *到const int *的轉(zhuǎn)換。
3、提升轉(zhuǎn)換。如:template<int a> class A{}; const short b=2; A<b> m;即從 short 到 int 的提升轉(zhuǎn)換
4、整值轉(zhuǎn)換。如:template<unsigned int a> class A{}; A<3> m; 即從 int 到 unsigned int 的轉(zhuǎn)換。
5、常規(guī)轉(zhuǎn)換。

注意:
(1) 可以為類模板的類型形參提供默認值,但不能為函數(shù)模板的類型形參提供默認值。函數(shù)模板和類模板都可以為模板的非類型形參提供默認值。
(2) 類模板的類型形參默認值形式為:template<class T1, class T2=int> class A{};為第二個模板類型形參 T2 提供 int 型的默認值。
(3) 類模板類型形參默認值和函數(shù)的默認參數(shù)一樣,如果有多個類型形參則從第一個形參設(shè)定了默認值之后的所有模板形參都要設(shè)定默認值,比如template<class T1=int, class T2>class A{};就是錯誤的,因為 T1 給出了默認值,而 T2 沒有設(shè)定。
(4) 在類模板的外部定義類中的成員時 template 后的形參表應(yīng)省略默認的形參類型。比如template<class T1, class T2=int> class A{public: void h();}; 定義方法為template<class T1,class T2> void A<T1,T2>::h(){}。

類模板實例化

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

類模板的實例化

類模板什么時候會被實例化呢?
① 當使用了類模板實例的名字,并且上下文環(huán)境要求存在類的定義時。
② 對象類型是一個類模板實例,當對象被定義時。此點被稱作類的實例化點。
③ 一個指針或引用指向一個類模板實例,當檢查這個指針或引用所指的對象時。

template<class Type>
class Graphics{};

void f1(Graphics<char>);// 僅是一個函數(shù)聲明,不需實例化

class Rect 
{
  Graphics<double>& rsd;// 聲明一個類模板引用,不需實例化
  Graphics<int> si;// si是一個Graphics類型的對象,需要實例化類模板
}

int main(){
  Graphcis<char>* sc;// 僅聲明一個類模板指針,不需實例化
  f1(*sc);//需要實例化,因為傳遞給函數(shù)f1的是一個Graphics<int>對象。
  int iobj=sizeof(Graphics<string>);//需要實例化,因為sizeof會計算Graphics<string>對象的大小,為了計算大小,編譯器必須根據(jù)類模板定義產(chǎn)生該類型。
}

2. 類模板的成員函數(shù)

要點:
① 類模板的成員函數(shù)可以在類模板的定義中定義(inline 函數(shù)),也可以在類模板定義之外定義(此時成員函數(shù)定義前面必須加上 template 及模板參數(shù))。
② 類模板成員函數(shù)本身也是一個模板,類模板被實例化時它并不自動被實例化,只有當它被調(diào)用或取地址,才被實例化。

3. 類模板的友元聲明

非模板友元類或友元函數(shù)

顧名思義,第一種聲明表示具體的類或函數(shù)。

綁定的友元類模板或函數(shù)模板

第二種聲明表示類模板的實例和它的友元之間是一種一對一的映射關(guān)系。
如圖:


綁定的友元類模板或函數(shù)模板

非綁定的友元類模板或函數(shù)模板

第三種聲明表示類模板的實例和它的友元之間是一種一對多的映射關(guān)系。
如圖:


非綁定的友元類模板或函數(shù)模板

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

4. 類模板的靜態(tài)數(shù)據(jù)成員、嵌套類型

類模板的靜態(tài)數(shù)據(jù)成員

要點:
① 靜態(tài)數(shù)據(jù)成員的模板定義必須出現(xiàn)在類模板定義之外。
② 類模板靜態(tài)數(shù)據(jù)成員本身就是一個模板,它的定義不會引起內(nèi)存被分配,只有對其實例化才會分配內(nèi)存。
③ 當程序使用靜態(tài)數(shù)據(jù)成員時,它被實例化,每個靜態(tài)成員實例都與一個類模板實例相對應(yīng),靜態(tài)成員的實例引用要通過一個類模板實例。

類模板的嵌套類型

要點:
① 在類模板中允許再嵌入模板,因此類模板的嵌套類也是一個模板,它可以使用外圍類模板的模板參數(shù)。
② 當外圍類模板被實例化時,它不會自動被實例化,只有當上下文需要它的完整類類型時,它才會被實例化。
③ 公有嵌套類型可以被用在類定義之外,這時它的名字前必須加上類模板實例的名字。

5. 成員模板

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

要點:
① 在一個類模板中定義一個成員模板,意味著該類模板的一個實例包含了可能無限多個嵌套類和無限多個成員函數(shù)。
② 只有當成員模板被使用時,它才被實例化。
③ 成員模板可以定義在其外圍類或類模板定義之外。

注意:類模板參數(shù)不一定與類模板定義中指定的名字相同。

6. 類模板的特化及部分特化

類模板的特化

先看下面的例子:

Template<class type>
Class Graphics
{
Public:
    void out(type figure){…}
};

Class Rect{…};

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

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

格式:template<> 成員函數(shù)特化定義

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

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

注意:
① 只有當通用類模板被聲明后,它的顯式特化才可以被定義。
② 若定義了一個類模板特化,則必須定義與這個特化相關(guān)的所有成員函數(shù)或靜態(tài)數(shù)據(jù)成員,此時類模板特化的成員定義不能以符號 template<> 作為打頭。(template<> 被省略)
③ 類模板不能夠在某些文件中根據(jù)通用模板定義被實例化,而在其他文件中卻針對同一組模板實參被特化。

類模板部分特化

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

template<int hi,int wid>
Class Graphics{…};

Template<int hi>//類模板的部分特化
Class Graphics<hi,90>{…};

格式:template<模板參數(shù)表>

注意:
① 部分特化的模板參數(shù)表只列出模板實參仍然未知的那些參數(shù)。
② 類模板部分特化是被隱式實例化的。編譯器選擇 “針對該實例而言最為特化的模板定義” 進行實例化,當沒有特化可被使用時,才使用通用模板定義。
例:Graphics<24,90> figure;
它即能從通用類模板定義被實例化,也能從部分特化的定義被實例化,但編譯器選擇的是部分特化來實例化模板。
③類模板部分特化必須有它自己對成員函數(shù)、靜態(tài)數(shù)據(jù)成員和嵌套類的定義。

7. 名字空間和類模板

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

Namespace cplusplus_primer
{
    Template<class type>
    Class Graphics{…};

    Template<class type>
    Type create()

    {…}
}

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

Void main()
{
    using cplusplus_primer::Graphics;
    Graphics<int> *pg=new Graphics<int>;
}

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

** 參考資料: **
C++中的類模板詳細講述
C++ 模板詳解(一)
C++ 模板詳解(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,674評論 1 51
  • 問題:什么是泛型編程?泛型編程的代表作品STL是一種高效、泛型、可交互操作的軟件組件。STL以迭代器 (Itera...
    認真學計算機閱讀 2,147評論 0 8
  • 收集非原創(chuàng)文章,如遇原作者,請私聊我,我會表明出處! 1--10 1. C++中什么數(shù)據(jù)分配在?;蚨眩o態(tài)存儲區(qū)以...
    Juinjonn閱讀 5,044評論 0 30
  • 青春似是一場華麗的虛張聲勢,來不及掩蓋,徜徉期中。開始期待,迷茫,狂傲。然后,狠狠地被摔碎,破敗不堪,狼藉一片。于...
    LQ木子閱讀 217評論 0 0
  • 文 | 文翼 本文為【愛惠物語】原創(chuàng)首發(fā) 轉(zhuǎn)載請聯(lián)系后臺授權(quán) 抄襲必究 聽說音樂和文章更配哦 人與人之間的關(guān)系...
    大文小翼閱讀 871評論 0 0

友情鏈接更多精彩內(nèi)容