C++ 模板簡介

C++ 模板簡介

一、模板

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

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

注意:
模板的聲明或定義只能在全局粱侣,命名空間或類范圍內(nèi)進(jìn)行。即不能在局部范圍蓖宦,函數(shù)內(nèi)進(jìn)行齐婴,比如不能在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 及其后面的標(biāo)識符構(gòu)成。非類型參數(shù)由一個普通參數(shù)構(gòu)成产喉,代表模板定義中的一個常量捂掰。
注意:
(1) 如果在全局域中聲明了與模板參數(shù)同名的變量,則該變量被隱藏掉曾沈。
(2) 模板參數(shù)名不能被當(dāng)作類模板定義中類成員的名字这嚣。
(3) 同一個模板參數(shù)名在模板參數(shù)表中只能出現(xiàn)一次。
(4) 在不同的類模板或聲明中塞俱,模板參數(shù)名可以被重復(fù)使用姐帚。
(5) 在類模板的前向聲明和定義中,模板參數(shù)的名字可以不同障涯。
(6) 類模板參數(shù)可以有缺省實參罐旗,給參數(shù)提供缺省實參的順序是先右后左膳汪。
(7) 類模板名可以被用作一個類型指示符。當(dāng)一個類模板名被用作另一個模板定義中的類型指示符時九秀,必須指定完整的實參表

類型參數(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 針對類模板丐黄,當(dāng)我們聲明類對象為: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仁堪,在運(yùn)行時哮洽,會對 3.2 進(jìn)行強(qiáng)制類型轉(zhuǎn)換為 3 。當(dāng)我們聲明類的對象為: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)用非類型模板形參的實參必須是一個常量表達(dá)式,即他必須能在編譯時計算出結(jié)果瑰谜。
    • **2.3 **任何局部對象欺冀,局部變量树绩,局部對象的地址,局部變量的地址都不是一個常量表達(dá)式隐轩,都不能用作非類型模板形參的實參饺饭。全局指針類型,全局變量职车,全局對象也不是一個常量表達(dá)式瘫俊,不能用作非類型模板形參的實參。
    • **2.4 **全局變量的地址或引用悴灵,全局對象的地址或引用const類型變量是常量表達(dá)式扛芽,可以用作非類型模板形參的實參。
    • **2.5 **sizeof表達(dá)式的結(jié)果是一個常量表達(dá)式积瞒,也能用作非類型模板形參的實參川尖。
    • **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) 可以為類模板的類型形參提供默認(rèn)值,但不能為函數(shù)模板的類型形參提供默認(rèn)值闯团。函數(shù)模板和類模板都可以為模板的非類型形參提供默認(rèn)值辛臊。
(2) 類模板的類型形參默認(rèn)值形式為:template<class T1, class T2=int> class A{};為第二個模板類型形參 T2 提供 int 型的默認(rèn)值。
(3) 類模板類型形參默認(rèn)值和函數(shù)的默認(rèn)參數(shù)一樣房交,如果有多個類型形參則從第一個形參設(shè)定了默認(rèn)值之后的所有模板形參都要設(shè)定默認(rèn)值彻舰,比如template<class T1=int, class T2>class A{};就是錯誤的,因為 T1 給出了默認(rèn)值,而 T2 沒有設(shè)定刃唤。
(4) 在類模板的外部定義類中的成員時 template 后的形參表應(yīng)省略默認(rèn)的形參類型隔心。比如template<class T1, class T2=int> class A{public: void h();}; 定義方法為template<class T1,class T2> void A<T1,T2>::h(){}

類模板實例化

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

類模板的實例化

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

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ù)本身也是一個模板恶复,類模板被實例化時它并不自動被實例化,只有當(dāng)它被調(diào)用或取地址速挑,才被實例化谤牡。

3. 類模板的友元聲明

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

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

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

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


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

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

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


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

注意:當(dāng)把非模板類或函數(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)存阵苇。
③ 當(dāng)程序使用靜態(tài)數(shù)據(jù)成員時壁公,它被實例化感论,每個靜態(tài)成員實例都與一個類模板實例相對應(yīng)绅项,靜態(tài)成員的實例引用要通過一個類模板實例。

類模板的嵌套類型

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

5. 成員模板

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

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

注意:類模板參數(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){…}

注意:
① 只有當(dāng)通用類模板被聲明后,它的顯式特化才可以被定義纳击。
② 若定義了一個類模板特化续扔,則必須定義與這個特化相關(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ù)宫蛆。
② 類模板部分特化是被隱式實例化的。編譯器選擇 “針對該實例而言最為特化的模板定義” 進(jìn)行實例化的猛,當(dāng)沒有特化可被使用時耀盗,才使用通用模板定義。
例:Graphics<24,90> figure;
它即能從通用類模板定義被實例化卦尊,也能從部分特化的定義被實例化叛拷,但編譯器選擇的是部分特化來實例化模板。
③類模板部分特化必須有它自己對成員函數(shù)岂却、靜態(tài)數(shù)據(jù)成員和嵌套類的定義忿薇。

7. 名字空間和類模板

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

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

    Template<class type>
    Type create()

    {…}
}

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市器联,隨后出現(xiàn)的幾起案子二汛,更是在濱河造成了極大的恐慌,老刑警劉巖拨拓,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肴颊,死亡現(xiàn)場離奇詭異,居然都是意外死亡渣磷,警方通過查閱死者的電腦和手機(jī)婿着,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來醋界,“玉大人竟宋,你說我怎么就攤上這事⌒畏模” “怎么了丘侠?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逐样。 經(jīng)常有香客問我蜗字,道長打肝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任挪捕,我火速辦了婚禮粗梭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘级零。我一直安慰自己断医,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布妄讯。 她就那樣靜靜地躺著孩锡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亥贸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天浇垦,我揣著相機(jī)與錄音炕置,去河邊找鬼。 笑死男韧,一個胖子當(dāng)著我的面吹牛朴摊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播此虑,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼甚纲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了朦前?” 一聲冷哼從身側(cè)響起介杆,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎韭寸,沒想到半個月后春哨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恩伺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年赴背,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晶渠。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凰荚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出褒脯,到底是詐尸還是另有隱情便瑟,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布憨颠,位于F島的核電站胳徽,受9級特大地震影響积锅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜养盗,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一缚陷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧往核,春花似錦箫爷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衩婚,卻和暖如春窜护,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背非春。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工柱徙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奇昙。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓护侮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親储耐。 傳聞我的和親對象是個殘疾皇子羊初,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容