溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++函數(shù)模板和類模板分析

發(fā)布時(shí)間:2021-11-30 16:45:54 來源:億速云 閱讀:121 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“C++函數(shù)模板和類模板分析”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1.函數(shù)模板

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

定義:

Template﹤類型參數(shù)表﹥返回類型 函數(shù)名 (形參表){函數(shù)體}

簡單實(shí)例,調(diào)用函數(shù)打印字符串或數(shù)字等。

普通函數(shù)形式:

#include <string> #include <iostream> void printstring(const std::string& str)  {         std::cout << str << std::endl; } int main() {         std::string str("Hello World");         printstring(str);     return 0; }//輸出:Hello World

模板函數(shù)形式:

#include <string> #include <iostream> using namespace std; template<typename T> void print(const T& var) {         cout << var << endl; } int main() {         string str("Hello World");         const int num=1234;     print(str);     print(num);     return 0; } //輸出:Hello World  //       1234

可以看出使用模板后的函數(shù)不僅可以輸出字符串形式還可以輸出數(shù)字形式的內(nèi)容。

上面兩個(gè)例子介紹了函數(shù)模板的簡單使用方法,但只有一個(gè)參數(shù),如果需要多個(gè)參數(shù),相應(yīng)的函數(shù)模板應(yīng)采用以下形式定義:

Template﹤類型1 變量1,類型2 變量2 ,&hellip;﹥返回類型 函數(shù)名 (形參表){函數(shù)體}

現(xiàn)在,為了看到模板時(shí)如何稱為函數(shù)的,我們假定min()函數(shù)接受各種類型的參數(shù),并找出其中的最小者,如果不采用模板技術(shù),則只能接受一個(gè)特定類型的參數(shù),如果希望也能接受其他類型的參數(shù),就需要對(duì)每一種類型的參數(shù)都定義一個(gè)同功能的函數(shù),其實(shí)為函數(shù)的重載,這里不在討論,但這將是一件非常讓人麻煩的事情。如:

普通定義:

#include <iostream.h> // 定義多態(tài)函數(shù),找出三個(gè)整數(shù)中最小的數(shù) int min0(int ii, int jj, int kk) {     int temp;     if((ii<jj)&&(ii<kk)){temp=ii;}     else if((jj<ii)&&(jj<kk)){temp=jj;    }     else{    temp=kk;    }     return temp; } // 定義多態(tài)函數(shù),找出三個(gè)小數(shù)中最小的數(shù) float min1(float ii, float jj, float kk) {     float temp;     if((ii<jj)&&(ii<kk)){temp=ii;}     else if((jj<ii)&&(jj<kk)){temp=jj;    }     else{    temp=kk;    }     return temp; }   // 定義多態(tài)函數(shù),找出三個(gè)子符中最小的字符 char min2(char ii, char jj, char kk) {     char temp;     if((ii<jj)&&(ii<kk))    {temp=ii;    }     else if((jj<ii)&&(jj<kk)){temp=jj;}         else{temp=kk;}     return temp; }   void main() {     int temp1=min0(100,20,30);     cout<<temp1<<endl;     float temp2=min1(10.60,10.64,53.21);     cout<<temp2<<endl;     char temp3=min2('c','a','C');     cout<<temp3<<endl; } //以換行形式輸出20  10.6  C

使用模板:

#include <iostream.h> // 定義函數(shù)模板,找出三個(gè)值中最小的值,與數(shù)據(jù)類型無關(guān) template <class T> T min(T ii, T jj, T kk) {     T temp;     if((ii<jj)&&(ii<kk)){temp=ii;}     else if((jj<ii)&&(jj<kk)){temp=jj;}     else{    temp=kk; }     return temp; } // 下面是主函數(shù) void main() {     cout<<min(100,20,30)<<endl;     cout<<min(10.60,10.64,53.21)<<endl;     cout<<min('c','a','C')<<endl; }

輸出結(jié)果同上,但可以清楚的看到二者之間的工作量大小之差距。

函數(shù)模板功能非常強(qiáng)大,但是有時(shí)候可能會(huì)陷入困境,加入待比較的函數(shù)模板沒有提供正確的操作符,則程序不會(huì)對(duì)此進(jìn)行編譯。為了避免這種錯(cuò)誤,可以使用函數(shù)模板和同名的非模板函數(shù)重載,這就是函數(shù)定制。函數(shù)模板與同名的非模板函數(shù)重載必須遵守以下規(guī)定:

尋找一個(gè)參數(shù)完全匹配的函數(shù),如有,則調(diào)用它

如果失敗,尋找一個(gè)函數(shù)模板,使其實(shí)例化,產(chǎn)生一個(gè)匹配的模板函數(shù),若有,則調(diào)用它

如果失敗,再試低一級(jí)的對(duì)函數(shù)重載的方法,例如通過類型轉(zhuǎn)換可產(chǎn)生的參數(shù)匹配等,若找到匹配的函數(shù),調(diào)用它

如果失敗,則證明這是一個(gè)錯(cuò)誤的調(diào)用

現(xiàn)在用上例的模板函數(shù)比較兩個(gè)字符串,但會(huì)出現(xiàn)問題:

#include <iostream.h> // 定義函數(shù)模板,找出三個(gè)值中最小的值,與數(shù)據(jù)類型無關(guān) template <class T> T min(T ii, T jj, T kk) {     T temp;     if((ii<jj)&&(ii<kk)){     temp=ii; }     else if((jj<ii)&&(jj<kk)){ temp=jj; }     else{    temp=kk;}     return temp; } void main()  {     cout<<min("anderson","Washington","Smith")<<endl; }

輸出anderson 與實(shí)際結(jié)果不否,原因在于編譯器會(huì)生成對(duì)字符串指針做比較的函數(shù),但比較字符串和比較字符串指針是不一樣的,為了解決此問題,我們可以定制函數(shù)模板,如:

#include <iostream> #include <string> using namespace std; // 定義函數(shù)模板,找出三個(gè)值中最小的值,與數(shù)據(jù)類型無關(guān) template <class T> T min(T ii, T jj, T kk) {     T temp;     if((ii<jj)&&(ii<kk)){        temp=ii;    }     else if((jj<ii)&&(jj<kk)){        temp=jj;    }     else    {        temp=kk;    }     return temp; } //非模板函數(shù)重載 const char* min(const char* ch2, const char* ch3,const char* ch4) {     const char* temp;     int result1 = strcmp(ch2,ch3);     int result2 = strcmp(ch2,ch4);     int result3 = strcmp(ch3,ch2);     int result4 = strcmp(ch3,ch4);     if((result1<0)&&(result2<0))    {        temp = ch2;    }     else if((result3<0)&&(result4<0))    {        temp=ch3;    }     else    {        temp=ch4;    }     return temp; } void main() {     cout<<min(100,20,30)<<endl;     cout<<min(10.60,10.64,53.21)<<endl;     cout<<min('c','a','C')<<endl;         cout<<min("anderson","Washington","Smith")<<endl; }

在VS 2010中,***一行會(huì)輸出Smith, 與結(jié)果先符。

注意:若上例在VC++ 6.0 中運(yùn)行,其結(jié)果***一行仍會(huì)輸出anderson,讀者可自己上機(jī)查看情況并分析原因。

下面給出一些實(shí)例:

#ifndef HEADER_MY #define HEADER_MY #include <string> #include <sstream> template<class T> T fromString(const std::string &s) {     std::istringstream is(s);     T t;     is>>t;     return t; } template<class T> std::string toString(const T &s) {     std::ostringstream t;     t<<s;     return t.str(); }   #endif     #include "HEADER.h"  #include <iostream>  #include <complex>  using namespace std;  int main()  {      int i = 1234;      cout << "i == \"" << toString(i) << "\"\n";      float x = 567.89;      cout << "x == \"" << toString(x) << "\"\n";      complex<float> c(1.0, 2.0);      cout << "c == \"" << toString(c) << "\"\n";      cout << endl;      i = fromString<int>(string("1234"));      cout << "i == " << i << endl;      x = fromString<float>(string("567.89"));      cout << "x == " << x << endl;      c = fromString< complex<float> >(string("(1.0,2.0)"));      cout << "c == " << c << endl;      return 0; }

模板實(shí)參推演

當(dāng)函數(shù)模板被調(diào)用時(shí),對(duì)函數(shù)實(shí)參類型的檢查決定了模板實(shí)參的類型和值的這個(gè)過程叫做模板實(shí)參推演。如template <class T> void h(T a){}; h(1); h(0.2);***個(gè)調(diào)用因?yàn)閷?shí)參是int型的,所以模板形參T被推演為int型,第二個(gè)T的類型則為double。

在使用函數(shù)模板時(shí),請(qǐng)注意以下幾點(diǎn):

在模板被實(shí)例化后,就會(huì)生成一個(gè)新的實(shí)例,這個(gè)新生成的實(shí)例不存在類型轉(zhuǎn)換。比如有函數(shù)模板template <class T>void H(T a){};int a=2; short b=3;***個(gè)調(diào)用H(a)生成一個(gè)int型的實(shí)例版本,但是當(dāng)調(diào)用h(b)的時(shí)候不會(huì)使用上次生成的int實(shí)例把short轉(zhuǎn)換為int,而是會(huì)另外生成一個(gè)新的short型的實(shí)例。

在模板實(shí)參推演的過程中有時(shí)類型并不會(huì)完全匹配,這時(shí)編譯器允許以下幾種實(shí)參到模板形參的轉(zhuǎn)換,這些轉(zhuǎn)換不會(huì)生成新的實(shí)例。

數(shù)組到指針的轉(zhuǎn)換或函數(shù)到指針的轉(zhuǎn)換:比如template<class T> void h(T * a){},int b[3]={1,2,3};h(b);這時(shí)數(shù)組b和類型T *不是完全匹配,但允許從數(shù)組到指針的轉(zhuǎn)換因此數(shù)組b被轉(zhuǎn)換成int *,而類型形參T被轉(zhuǎn)換成int,也就是說函數(shù)體中的T被替換成int。

限制修飾符轉(zhuǎn)換:即把const或volatile限定符加到指針上。比如template<class T> void h(const T* a){},int b=3; h(&b);雖然實(shí)參&b與形參const T*不完全匹配,但因?yàn)樵试S限制修飾符的轉(zhuǎn)換,結(jié)果就把&b轉(zhuǎn)換成const int *。而類形型參T被轉(zhuǎn)換成int。如果模板形參是非const類型,則無論實(shí)參是const類型還是非const類型調(diào)用都不會(huì)產(chǎn)生新的實(shí)例。

到一個(gè)基類的轉(zhuǎn)換(該基類根據(jù)一個(gè)類模板實(shí)例化而來):比如tessmplate<class T1>class A{}; template<class T1> class B:public A<T1>{}; template<class T2> void h(A<T2>& m){},在main函數(shù)中有B<int> n; h(n);函數(shù)調(diào)用的子類對(duì)象n與函數(shù)的形參A<T2>不完全匹配,但允許到一個(gè)基類的轉(zhuǎn)換。在這里轉(zhuǎn)換的順序?yàn)?,首先把子類?duì)象n轉(zhuǎn)換為基類對(duì)象A<int>,然后再用A<int>去匹配函數(shù)的形參A<T2>&,所以***T2被轉(zhuǎn)換為int,也就是說函數(shù)體中的T將被替換為int。

對(duì)于函數(shù)模板而言不存在h(int,int)這樣的調(diào)用,不能在函數(shù)調(diào)用的參數(shù)中指定模板形參的類型,對(duì)函數(shù)模板的調(diào)用應(yīng)使用實(shí)參推演來進(jìn)行,即只能進(jìn)行h(2,3)這樣的調(diào)用,或者int a, b; h(a,b)。

模板實(shí)參推演實(shí)例,說明內(nèi)容較長,采用注釋形式,但代碼較亂:

#include <iostream> using namespace std; template<class T>void h(T a){cout<<" h()"<<typeid(T).name()<<endl;}  //帶有一個(gè)類型形參T的模板函數(shù)的定義方法,typeid(變量名).name()為測試變量類型的語句。 template<class T>void k(T a,T b){T c;cout<<" k()"<<typeid(T).name()<<endl;} //注意語句T c。模板類型形參T可以用來聲明變量,作為函數(shù)的反回類型,函數(shù)形參等凡是類類型能使用的地方。 template<class T1,class T2> void f(T1 a, T2 b){cout<<" f()"<<typeid(T1).name()<<","<<typeid(T2).name()<<endl;}   //定義帶有兩個(gè)類型形參T1,T2的模板函數(shù)的方法template<class T> void g(const T* a){T b;cout<<" g()"<<typeid(b).name()<<endl;}  //template<class T1,class T2=int> void g(){}  //錯(cuò)誤,默認(rèn)模板類型形參不能用于函數(shù)模板,只能用于類模板上。 //main函數(shù)開始 int main() { // template<class T>void h(){} //錯(cuò)誤,模板的聲明或定義只能在全局,命名空間或類范圍內(nèi)進(jìn)行。即不能在局部范圍,函數(shù)內(nèi)進(jìn)行。 //函數(shù)模板實(shí)參推演示例。 // h(int); //錯(cuò)誤,對(duì)于函數(shù)模板而言不存在h(int,int)這樣的調(diào)用,不能在函數(shù)調(diào)用的參數(shù)中指定模板形參的類型,對(duì)函數(shù)模板的調(diào)用應(yīng)使用實(shí)參推演來進(jìn)行,即只能進(jìn)行h(2,3)這樣的調(diào)用,或者int a, b; h(a,b)。 //h函數(shù)形式為:template<class T>void h(T a) h(2);//輸出" h() int"使用函數(shù)模板推演,在這里數(shù)值2為int型,所以把類型形參T推演為int型。 h(2.0);//輸出" h() double",因?yàn)?.0為double型,所以將函數(shù)模板的類型形參推演為double型 //k函數(shù)形式為:template<class T>void k(T a,T b) k(2,3);//輸出" k() int" //k(2,3.0);錯(cuò)誤,模板形參T的類型不明確,因?yàn)閗()函數(shù)***個(gè)參數(shù)類型為int,第二個(gè)為double型,兩個(gè)形參類型不一致。 //f函數(shù)的形式為:template<class T1,class T2> void f(T1 a, T2 b) f(3,4.0);//輸出" f() int,double",這里不存在模板形參推演錯(cuò)誤的問題,因?yàn)槟0搴瘮?shù)有兩個(gè)類型形參T1和T2。在這里將T1推演為int,將T2推演為double。 int a=3;double b=4; f(a,b); //輸出同上,這里用變量名實(shí)現(xiàn)推板實(shí)參的推演。 //模板函數(shù)推演允許的轉(zhuǎn)換示例,g函數(shù)的形式為template<class T> void g(const T* a) int a1[2]={1,2};g(a1); //輸出" g() int",數(shù)組的地址和形參const T*不完全匹配,所以將a1的地址T &轉(zhuǎn)換為const T*,而a1是int型的,所以***T推演為int。 g(&b); //輸出" g() double",這里和上面的一樣,只是把類型T轉(zhuǎn)換為double型。 h(&b); //輸出" h() double *"這里把模參類型T推演為double *類型。 return 0; }

函數(shù)模板的顯示實(shí)例化

隱式實(shí)例化:比如有模板函數(shù)template<class T> void h(T a){}。h(2)這時(shí)h函數(shù)的調(diào)用就是隱式實(shí)例化,既參數(shù)T的類型是隱式確定的。

函數(shù)模板顯示實(shí)例化聲明:其語法是:template  函數(shù)反回類型 函數(shù)名<實(shí)例化的類型> (函數(shù)形參表); 注意這是聲明語句,要以分號(hào)結(jié)束。例如:template  void h<int> (int a);這樣就創(chuàng)建了一個(gè)h函數(shù)的int 實(shí)例。再如有模板函數(shù)template<class T> T h( T a){},注意這里h函數(shù)的反回類型為T,顯示實(shí)例化的方法為template int h<int>(int a); 把h模板函數(shù)實(shí)例化為int 型。

對(duì)于給定的函數(shù)模板實(shí)例,顯示實(shí)例化聲明在一個(gè)文件中只能出現(xiàn)一次。

在顯示實(shí)例化聲明所在的文件中,函數(shù)模板的定義必須給出,如果定義不可見,就會(huì)發(fā)生錯(cuò)誤。

注意:不能在局部范圍類顯示實(shí)例化模板,實(shí)例化模板應(yīng)放在全局范圍內(nèi),即不能在main函數(shù)等局部范圍中實(shí)例化模板。因?yàn)槟0宓穆暶骰蚨x不能在局部范圍或函數(shù)內(nèi)進(jìn)行。

顯示模板實(shí)參:

1、顯示模板實(shí)參:適用于函數(shù)模板,即在調(diào)用函數(shù)時(shí)顯示指定要調(diào)用的時(shí)參的類型。

2、格式:顯示模板實(shí)參的格式為在調(diào)用模板函數(shù)的時(shí)候在函數(shù)名后用<>尖括號(hào)括住要顯示表示的類型,比如有模板函數(shù)template<class T> void h(T a, T b){}。則h<double>(2, 3.2)就把模板形參T顯示實(shí)例化為double類型。

3、顯示模板實(shí)參用于同一個(gè)模板形參的類型不一致的情況。比如template<class T> void h(T a, T b){},則h(2, 3.2)的調(diào)用會(huì)出錯(cuò),因?yàn)閮蓚€(gè)實(shí)參類型不一致,***個(gè)為int 型,第二個(gè)為double型。而用h<double>(2, 3.2)就是正確的,雖然兩個(gè)模板形參的類型不一致但這里把模板形參顯示實(shí)例化為double類型,這樣的話就允許進(jìn)行標(biāo)準(zhǔn)的隱式類型轉(zhuǎn)換,即這里把***個(gè)int 參數(shù)轉(zhuǎn)換為double類型的參數(shù)。

4、顯示模板實(shí)參用法二:用于函數(shù)模板的反回類型中。例如有模板函數(shù)template<class T1, class T2, class T3> T1 h(T2 a, T3 b){},則語句int a=h(2,3)或h(2,4)就會(huì)出現(xiàn)模板形參T1無法推導(dǎo)的情況。而語句int h(2,3)也會(huì)出錯(cuò)。用顯示模板實(shí)參就參輕松解決這個(gè)問題,比如h<int, int, int>(2,3)即把模板形參T1實(shí)例化為int 型,T2和T3也實(shí)例化為int 型。

5、顯示模板實(shí)參用法三:應(yīng)用于模板函數(shù)的參數(shù)中沒有出現(xiàn)模板形參的情況。比如template<class  T>void h(){}如果在main函數(shù)中直接調(diào)用h函數(shù)如h()就會(huì)出現(xiàn)無法推演類型形參T的類型的錯(cuò)誤,這時(shí)用顯示模板實(shí)參就不會(huì)出現(xiàn)這種錯(cuò)誤,調(diào)用方法為h<int>(),把h函數(shù)的模板形參實(shí)例化為int 型,從而避免這種錯(cuò)誤。

6、顯示模板實(shí)參用法四:用于函數(shù)模板的非類型形參。比如template<class T,int a> void h(T b){},而調(diào)用h(3)將出錯(cuò),因?yàn)檫@個(gè)調(diào)用無法為非類型形參推演出正確的參數(shù)。這時(shí)正確調(diào)用這個(gè)函數(shù)模板的方法為h<int, 3>(4),首先把函數(shù)模板的類型形參T推演為int 型,然后把函數(shù)模板的非類型形參int a用數(shù)值3來推演,把變量a設(shè)置為3,然后再把4傳遞給函數(shù)的形參b,把b設(shè)置為4。注意,因?yàn)閕nt a是非類型形參,所以調(diào)用非類型形參的實(shí)參應(yīng)是編譯時(shí)常量表達(dá)式,不然就會(huì)出錯(cuò)。

7、在使用顯示模板實(shí)參時(shí),我們只能省略掉尾部的實(shí)參。比如template<class T1, class T2, class T3> T1 h(T2 a, T3 b){}在顯示實(shí)例化時(shí)h<int>(3, 3.4)省略了***兩個(gè)模板實(shí)參T2和T3,T2和T3由調(diào)用時(shí)的實(shí)參3和3.4隱式確定為int 型和double型,而T1被顯示確定為int 型。h<int, , double><2,3.4>是錯(cuò)誤的,只能省略尾部的實(shí)參。

8、顯示模板實(shí)參***用在存在二義性或模板實(shí)參推演不能進(jìn)行的情況下。

下面來看看實(shí)例:

#include <iostream> using namespace std; template<class T>void g1(T a, T b){cout<<"hansu g1()"<<typeid(T).name()<<endl;} template<class T1,class T2,class T3>T1 g2(T2 a,T3 b) {T1 c=a;cout<<"hansug2()"<<typeid(T1).name()<<typeid(T2).name()<<typeid(T3).name()<<endl; return c;} template<class T1,class T2> void g3 ( T1 a ) {cout<<"hansu g3()"<<typeid(T1).name()<<typeid(T2).name()<<endl;} template<class T1,int a> void g4(T1 b, double c){cout<<"hansu g4()"<<typeid(T1).name()<<typeid(a).name()<<endl;} template<class T1,class T2> class A{public:void g();}; //模板顯示實(shí)例化示例。 //因?yàn)槟0宓穆暶骰蚨x不能在局部范圍或函數(shù)內(nèi)進(jìn)行。所以模板實(shí)例化都應(yīng)在全局范圍內(nèi)進(jìn)行。 template void g1<double>(double a,double b); //把函數(shù)模板顯示實(shí)例化為int型。 template class A<double,double>; //顯示實(shí)例化類模板,注意后面沒有對(duì)象名,也沒有{}大括號(hào)。 //template class A<int,int>{};  //錯(cuò)誤,顯示實(shí)例化類模板后面不能有大括號(hào){}。 //template class A<int,int> m;  //錯(cuò)誤,顯示實(shí)例化類模板后面不能有對(duì)象名。 //main函數(shù)開始 int main() {//顯示模板實(shí)參示例。顯示模板實(shí)參適合于函數(shù)模板 //1、顯示模板實(shí)參用于同一個(gè)模板形參的類型不一致的情況。函數(shù)g1形式為template<class T>void g1(T a, T b) g1<double>(2,3.2);//輸出"hansu g1() int"兩個(gè)實(shí)參類型不一致,***個(gè)為int第二個(gè)為double。但這里用顯示模板實(shí)參把類型形參T指定為double,所以***個(gè)int型的實(shí)參數(shù)值2被轉(zhuǎn)換為double類型。 //g1(2,3.2);錯(cuò)誤,這里沒有用顯式模板實(shí)參。所以兩個(gè)實(shí)參類型不一致。 //2、用于函數(shù)模板的反回類型中。函數(shù)g2形式為template<class T1,class T2,class T3> T1 g2(T2 a,T3 b) //g2(2,3);錯(cuò)誤,無法推演類型形參T1。 //int g2(2,3);錯(cuò)誤,不能以這種方法試圖推導(dǎo)類型形參T1為int型。 //int a=g2(2,3);錯(cuò)誤,以這種方式試圖推演出T1的類型為int也是錯(cuò)誤的。 g2<int,int,int>(2,3);//正確,將T1,T2,T3 顯示指定為int型。輸出"hansu g2() intintint" //3、應(yīng)用于模板函數(shù)的參數(shù)中沒有出現(xiàn)模板形參的情況其中包括省略的用法,函數(shù)g3的形式為template<class T1,class T2> void g3(T1 a) //g3(2);錯(cuò)誤,無法為函數(shù)模板的類型形參T2推演出正確的類型 //g3(2,3);錯(cuò)誤,豈圖以這種方式為T2指定int型是錯(cuò)誤的,因?yàn)楹瘮?shù)只有一個(gè)參數(shù)。 //g3<,int>(2);錯(cuò)誤,這里起圖用數(shù)值2來推演出T1為int型,而省略掉***個(gè)的顯示模板實(shí)參,這種方法是錯(cuò)誤的。在用顯示模板實(shí)參時(shí),只能省略掉尾部的實(shí)參。 //g3<int>(2);錯(cuò)誤,雖然用了顯示模板實(shí)參方法,省略掉了尾部的實(shí)參,但該方法只是把T1指定為int型,仍然無法為T2推演正確的類型。 g3<int,int>(2);//正確,顯示指定T1和T2的類型都為int型。 //4、用于函數(shù)模板的非類型形參。g4函數(shù)的形式為template<class T1,int a> void g4(T1 b,double c) //g4(3,3.2);錯(cuò)誤,雖然指定了兩個(gè)參數(shù),但是這里仍然無法為函數(shù)模板的非類型形參int a推演出正確的實(shí)參。因?yàn)榈诙€(gè)函數(shù)參數(shù)x.2是傳遞給函數(shù)的參數(shù)double c的,而不是函數(shù)模板的非類型形參int a。 //g4(3,2);錯(cuò)誤,起圖以整型值把實(shí)參傳遞給函數(shù)模板的非類型形參是不行的,這里數(shù)值2會(huì)傳遞給函數(shù)形參double c并把int型轉(zhuǎn)換為double型。所以非類型形參int a仍然無實(shí)參。 //int d=1; g4<int ,d >(3,3.2); //錯(cuò)誤,調(diào)用方法正確,但對(duì)于非類型形參要求實(shí)參是一個(gè)常量表達(dá)式,而局部變量c是非常量表達(dá)式,不能做為非類型形參的實(shí)參,所以錯(cuò)誤。 g4<int,1>(2,3.2);//正確,用顯示模板實(shí)參,把函數(shù)模板的類型形參T1設(shè)為int型,把數(shù)值1傳給非類型形參int a,并把a(bǔ)設(shè)為1,把數(shù)值2 傳給函數(shù)的***個(gè)形參T1 b并把b設(shè)為2,數(shù)值?.2傳給函數(shù)的第二個(gè)形參double c并把c設(shè)為?.2。 const int d=1; g4<int,d>(2,3.2);//正確,這里變量d是const常量,能作為非類型形參的實(shí)參,這里參數(shù)的傳遞方法同上面的語句。 return 0; }

顯示具體化(模板特化,模板說明) 和函數(shù)模板的重載

1、具體化或特化或模板說明指的是一個(gè)意思,就是把模板特殊化,比如有模板template<class T>void h(T a){},這個(gè)模板適用于所有類型,但是有些特殊類型不需要與這個(gè)模板相同的操作或者定義,比如int 型的h實(shí)現(xiàn)的功能和這個(gè)模板的功能不一樣,這樣的話我們就要重定義一個(gè)h模板函數(shù)的int 版本,即特化版本。

特化函數(shù)模板:

2、顯示特化格式為:template<>  反回類型函數(shù)名<要特化的類型>(參數(shù)列表) {函數(shù)體},顯示特化以template<>開頭,表明要顯示特化一個(gè)模板,在函數(shù)名后<>用尖括號(hào)括住要特化的類型版本。比如template <class T> void h(T a){},其int 類型的特化版本為template<> void h<int>(int a){},當(dāng)出現(xiàn)int 類型的調(diào)用時(shí)就會(huì)調(diào)用這個(gè)特化版本,而不會(huì)調(diào)用通用的模板,比如h(2),就會(huì)調(diào)用int 類型的特化版本。

3、如果可以從實(shí)參中推演出模板的形參,則可以省略掉顯示模板實(shí)參的部分。比如:template<> void h(int a){}。注意函數(shù)h后面沒有<>符號(hào),即顯示模板實(shí)參部分。

4、對(duì)于反回類型為模板形參時(shí),調(diào)用該函數(shù)的特化版本必須要用顯示模板實(shí)參調(diào)用,如果不這樣的話就會(huì)出現(xiàn)其中一個(gè)形參無法推演的情況。如template<class T1,class T2,class T3> T1 h(T2 a,T3 b){},有幾種特化情況:

情況一:template<> int h<int,int>(int a, in b){}該情況下把T1,T2,T3的類型推演為int 型。在主函數(shù)中的調(diào)用方式應(yīng)為h<int>(2,3)。

情況二:template<> int h(int a, int b){},這里把T2,T2推演為int 型,而T1為int 型,但在調(diào)用時(shí)必須用顯示模板實(shí)參調(diào)用,且在<>尖括號(hào)內(nèi)必須指定為int 型,不然就會(huì)調(diào)用到通用函數(shù)模板,如h<int>(2,3)就會(huì)調(diào)用函數(shù)模板的特化版本,而h(2,3)調(diào)用會(huì)出錯(cuò)。h<double>(2,3)調(diào)用則會(huì)調(diào)用到通用的函數(shù)模板版本。

這幾種情況的特化版本是錯(cuò)誤的,如template<> T1 h(int a,int b){},這種情況下T1會(huì)成為不能識(shí)別的名字,因而出現(xiàn)錯(cuò)誤,template<> int h<double>(int a,int b){}在這種情況下反回類型為int 型,把T1確定為int 而尖括號(hào)內(nèi)又把T1確定為double型,這樣就出現(xiàn)了沖突。

5、具有相同名字和相同數(shù)量反回類型的非模板函數(shù)(即普通函數(shù)),也是函數(shù)模板特化的一種情況,這種情況將在后面參數(shù)匹配問題時(shí)講解。

函數(shù)模板重載(函數(shù)定制):

1、函數(shù)模板可以重載,注意類模板不存在重載問題,也就是說出現(xiàn)這兩條語句時(shí)template<class  T>class A{};

template<classT1,class T2>class A{};將出錯(cuò)。

2、模板函數(shù)重載的形式為:template<class T> void h(T a, int b){}。Template<class T>void h(T a, double b){}等。

3、重載模板函數(shù)要注意二義性問題,比如template<class T> void h(T a, int b){}和template<class T>void h(T a, T b){}這兩個(gè)版本就存在二義性問題,當(dāng)出現(xiàn)語句h(2,3)時(shí)就不知道調(diào)用哪個(gè)才正確,在程序中應(yīng)避免這種情況出現(xiàn)。

4、重載函數(shù)模板的第二個(gè)二義性問題是template<class T>void h(T a, T b){}與template<class T1, class T2>void h(T1 a,T2 b){},當(dāng)出現(xiàn)h(2,4)這樣的調(diào)用時(shí)就會(huì)出現(xiàn)二義性。解決這個(gè)問題的方法是使用顯示模板實(shí)參,比如要調(diào)用***個(gè)h函數(shù),可以使用語法h<int>(2,3),調(diào)用第二個(gè)h函數(shù)的方法為h<int, int>(2,3)。

5、函數(shù)模板的特化也可以理解為函數(shù)模板重載的一種形式。只是特化以template<>開始。

6、重載的特殊情況:比如template<class T1,class T2> void h(T1 a, T2 b){},還有個(gè)版本如template<class T1>void h(T1 a, int b){}這里兩個(gè)函數(shù)具有兩同的名字和相同的形參數(shù)量,但形參的類型不同,可以認(rèn)為第二個(gè)版本是***個(gè)版本的重載版本。

7、函數(shù)模板的重載和特化很容易混曉,因?yàn)樘鼗芟袷且粋€(gè)函數(shù)的重載版本,只是開頭以template<>開始而已。

特化類模板

6、特化整個(gè)類模板:比如有template<class T1,class T2> class A{};其特化形式為template<> class A<int, int>{};特化形式以template<>開始,這和模板函數(shù)的形式相同,在類名A后跟上要特化的類型。

7、在類特化的外部定義成員的方法:比如template<class T> class A{public: void h();};類A特化為template<>  class A<int>{public: void h();};在類外定義特化的類的成員函數(shù)h的方法為:void A<int>::h(){}。在外部定義類特化的成員時(shí)應(yīng)省略掉template<>。

8、類的特化版本應(yīng)與類模板版本有相同的成員定義,如果不相同的話那么當(dāng)類特化的對(duì)象訪問到類模板的成員時(shí)就會(huì)出錯(cuò)。因?yàn)楫?dāng)調(diào)用類的特化版本創(chuàng)建實(shí)例時(shí)創(chuàng)建的是特化版本的實(shí)例,不會(huì)創(chuàng)建類模板的實(shí)例,特化版本如果和類的模板版本的成員不一樣就有可能出現(xiàn)這種錯(cuò)誤。比如:模板類A中有成員函數(shù)h()和f(),而特化的類A中沒有定義成員函數(shù)f(),這時(shí)如果有一個(gè)特化的類的對(duì)象訪問到模板類中的函數(shù)f()時(shí)就會(huì)出錯(cuò),因?yàn)樵谔鼗惖膶?shí)例中找不到這個(gè)成員。

9、類模板的部分特化:比如有類模板template<class T1, class T2> class A{};則部分特化的格式為template<class T1> class A<T1, int>{};將模板形參T2特化為int 型,T1保持不變。部分特化以template開始,在<>中的模板形參是不用特化的模板形參,在類名A后面跟上要特化的類型。如果要特化***個(gè)模板形參T1,則格式為template<class T2> class A<int, T2>{};部分特化的另一用法是template<class T1> class A<T1,T1>{};將模板形參T2也特化為模板形參T1的類型。

10、在類部分特化的外面定義類成員的方法:比如有部分特化類template<class T1> class A<T1,int>{public: void h();};則在類外定義的形式為template<class T1> void A<T1,int>::h(){}。注意當(dāng)在類外面定義類的成員時(shí)template 后面的模板形參應(yīng)與要定義的類的模板形參一樣,這里就與部分特化的類A的一樣template<class T1>。

其他說明:

11、可以對(duì)模板的特化版本只進(jìn)行聲明,而不定義。比如template<> void h<int>(int a);注意,聲明時(shí)后面有個(gè)分號(hào)。

12、在調(diào)用模板實(shí)例之前必須要先對(duì)特化的模板進(jìn)行聲明或定義。一個(gè)程序不允許同一模板實(shí)參集的同一模板既有顯示特化又有實(shí)例化。比如有模板template<class T> void h(T a){}在h(2)之前沒有聲明該模板的int 型特化版本,而是在調(diào)用該模板后定義該模板的int 型特化版本,這時(shí)程序不會(huì)調(diào)用該模板的特化版本,而是調(diào)用該模板產(chǎn)生一個(gè)新的實(shí)例。這里就有一個(gè)問題,到底是調(diào)用由h(2)產(chǎn)生的實(shí)例版本呢還是調(diào)用程序中的特化版本。

13、注意:因?yàn)槟0宓穆暶骰蚨x不能在局部范圍或函數(shù)內(nèi)進(jìn)行。所以特化類模板或函數(shù)模板都應(yīng)在全局范圍內(nèi)進(jìn)行。

14、在特化版本中模板的類型形參是不可見的。比如template<> void h<int,int>(int a,int b){T1 a;}就會(huì)出現(xiàn)錯(cuò)誤,在這里模板的類型形參T1在函數(shù)模板的特化版本中是不可見的,所以在這里T1是未知的標(biāo)識(shí)符,是錯(cuò)誤的。

#include <iostream> using namespace std; //函數(shù)模板特化和類模板特化示例 //定義函數(shù)g1,g2和類A template<class T1,class T2> void g1(T1 a,T2 b){cout<<"g1"<<endl;} template<class T1,class T2,class T3>T1 g2(T2 a,T3 b){  int c=1;cout<<"g2"<<endl;return c;} template<class T1,class T2,class T3>class A{public:void h();} //函數(shù)模板的特化定義。函數(shù)模板的特化可以理解為函數(shù)模板重載的另一種形式。 //下式為g1的類型形參顯示指定其類型,把T1,T2在模板實(shí)參的尖括號(hào)中設(shè)為int型。 template<> void g1<int,int>(int a,int b){cout<<"g1一"<<endl;} //下式顯示設(shè)定g1的類型形參T1,并設(shè)為int型,T2由函數(shù)參數(shù)double推演為double型。 template<> void g1<int>(int a,double b){cout<<"g1二"<<endl;}   template<> void g1(double a,double b){cout<<"g1三"<<endl;} //g1的類型形參都由g1的形參推演出來。 //template<> void g1<int>(double a,int b){cout<<"g&bull;一"<<endl;}  //錯(cuò)誤,在顯示模板實(shí)參的尖括號(hào)中顯示把類型形參T1的類型設(shè)為int型,而又在函數(shù)的形參中把類型形參T1的類型推演為double型,這樣就發(fā)生了沖突,出現(xiàn)錯(cuò)誤。 template<> int g2<int>(int a,int b){int c=1;cout<<"g2一"<<endl;return c;}  template<>double g2(int a,int b){int c=1;cout<<"g2二"<<endl;return c;} //注意,下式正確,該式并不是對(duì)函數(shù)模板g2的部分特化,而是g2的重載。 //template<class T2> int g2(int a, T2 b){int c=1;cout<<"g2三"<<endl;return c;} //下式錯(cuò)誤,函數(shù)反回類型和<double>尖括號(hào)中的double類型不同,發(fā)生沖突。 //template<> int g2<double>(int a,int b){int c=1;cout<<"two"<<endl;return c;}  //下式錯(cuò)誤,函數(shù)模板的類型形參在特化版本中是不可見的,也就是說這里的會(huì)把類型形參T1理解為未聲明的標(biāo)識(shí)符 //template<> T1 g2<int>(int a,int b){int c=1;cout<<"two"<<endl;return c;}  //類模板的特化和部分特化  template<>class A<int,int,int>{public:void h();}//特化整個(gè)類模板的格式,注意類名后的尖括號(hào)中必須指定所有的類模板的類型形參。 //template<> class A<int>{}; //錯(cuò)誤,在特化的類名后的尖括號(hào)中指定的類模板類型形參的數(shù)量不夠。要想只特化其中一個(gè)類模板的類型形參,就要使用類模板的部分特化。 template<class T1,class T3>class A<T1,double,T3>{public:void h();}//特化T2,而T1和T?不特化,注意尖括號(hào)中的類型形參是不特化的形參。 //在類模板的特化或部分特化版本的外部定義成員函數(shù)的方法。 void A<int,int,int>::h(){cout<<"class A tehua"<<endl;} /*  T1 c; 錯(cuò)誤,在特化版本中模板的類型形參是不可見的,也就是說在這里 T1是未聲明的標(biāo)識(shí)符。*/ //template<> void A<int,int,int>::h(){} //錯(cuò)誤,在類模板的特化版本外面定義類模板的成員時(shí)應(yīng)省略掉template<> template<class T1,class T3>void A<T1,double,T3)::h(){cout<<"class A bute"<<endl;} template<class T1,class T2,class T3>void A<T1,T2,T3>::h(){cout<<"class A putong"<<endl;} //定義普通類模板中的成員函數(shù)。 //main函數(shù)開始 int main() {   //特化的函數(shù)模板的調(diào)用方式。     g1(2,2); //輸出"g1一",調(diào)用函數(shù)模板g1的***個(gè)特化版本template<> void g1<int,int>(int a,int b){cout<<"g1一"<<endl;}     g1(2,3.2); //輸出"g1二",調(diào)用函數(shù)模板g1的第二個(gè)特化版本template<> void g1<int>(int a,double b){cout<<"g1二"<<endl;}     g1(3.3,4.4); //輸出"g1三",調(diào)用函數(shù)模板g1的第三個(gè)特化版本template<> void g1(double a,double b){cout<<"g1三"<<endl;}     g1<double>(3,2.3);//輸出"g1三",這里用顯示模板實(shí)參把***個(gè)實(shí)參指定為double型,這樣g1的兩個(gè)實(shí)參都是double型,所以將調(diào)用g1的第三個(gè)特化版本。     //g2(3,3); 錯(cuò)誤,在調(diào)用反回類型為類型形參的時(shí)候必須用顯示模板實(shí)參的形式為反回類型的形參顯示指定類型。在這里就會(huì)出現(xiàn)無法為T1確定類型的情況。     g2<int>(2,3);//正確,把g2的類型形參T1設(shè)顯示指定為int,調(diào)用g2的***個(gè)特化版本。template<> int g2<int>(int a,int b){int c=1;cout<<"g2一"<<endl;return c;}     g2<double>(2,3);//正確,把g2的類型形參T1設(shè)顯示指定為double,調(diào)用g2的第二個(gè)特化版本。template<> double g2(int a,int b){int c=1;cout<<"g2二"<<endl;return c;}     g2<char>(2,3);//正確,把g2的類型形參T1設(shè)顯示指定為char,對(duì)于char版本的g2函數(shù)沒有特化版本,因此調(diào)用g2的通用版本。     //    template<class T1,class T2,class T3>T1 g2(T2 a,T3 b) {int c=1;cout<<"g2"<<endl;return c;}    // 類模板特化和部分特化的調(diào)用。      A<int,int,int> m1; m1.h();//正確,調(diào)用類模板的特化版本。      A<int,double,int> m; m.h(); //正確,調(diào)用類模板的部分特化版本。            //A<int,int> m2; //錯(cuò)誤,類模板有三個(gè)類型形參,這里只提供了兩個(gè),數(shù)量不夠,錯(cuò)誤。      A<double,double,int> m3; m3.h();//調(diào)用類A的部分特化版本。       A<double,int,int> m4; m4.h();//調(diào)用類A的普通版本,在這里沒有A<double,int,int>型的特化或者部分特化版本可用。       return 0; }

1. 類模板

在此之前我們來看看模板的形參。因?yàn)楹瘮?shù)模板的參數(shù)相對(duì)比較簡單,故將此內(nèi)容放置于類模板中。模板形參有三種類型:類型形參、非類型形參和模板形參。先分別解釋如下:

類型形參。即由關(guān)鍵字class 或 typename后接的說明符構(gòu)成,如template <class T>void function(T a);其中T就是類型形參。類型形參的名字由用戶自定義,只要是合法的標(biāo)識(shí)符即可。

非類型形參。模板的非類型形參也就是內(nèi)置類型形參,如template<class T,int a>class B{};其中int a就是非類型形參。非類型形參在模板定義的內(nèi)部是常量值,也就是說非類型形參在模板內(nèi)部是常量。使用非類型形參應(yīng)注意以下幾點(diǎn):

非類型形參只能是整型、指針和應(yīng)用。如:double,string,string **等都是不允許的,但是double & ,double *是正確的。

調(diào)用非類型模板形參的實(shí)參必須是一個(gè)常量表達(dá)式,即在編譯時(shí)就能確定其結(jié)果。任何局部對(duì)象、局部變量、局部變量地址、局部對(duì)象地址等都不是一個(gè)常量表達(dá)式,都不能用作非類型模板形參的實(shí)參。全局指針類型、全局變量、全局對(duì)象也不是一個(gè)常量表達(dá)式,不能用作非類型形參的實(shí)參。但全局變量的地址、全局對(duì)象的地址或應(yīng)用const類型的變量時(shí)常量表達(dá)式,可用作非類型模板形參的實(shí)參。Sizeof表達(dá)式的結(jié)果也是一個(gè)常量表達(dá)式,同樣也可以用作非類型模板形參的實(shí)參。如:

Template <class T,int a>class A{};如果有int b,這時(shí)A<int,b> m;就會(huì)出錯(cuò),因?yàn)閎不是常量,如果有const int b;這時(shí)A<int ,b>就是正確的。

非類型形參一般不用于函數(shù)模板中。比如有函數(shù)模板template <class T,int a>void h(T,b){};若使用h(2)調(diào)用就會(huì)出錯(cuò),無法為非類型形參a推演出參數(shù)的錯(cuò)誤。對(duì)這種函數(shù)模板可以采用顯示模板實(shí)參來解決,如h<int ,3>(2),這樣就把非類型形參a設(shè)置為整數(shù)3。顯示模板參數(shù)將在后面介紹。

非類型模板形參和實(shí)參間允許轉(zhuǎn)換。具體如下;

允許從數(shù)組到指針,從函數(shù)到指針的轉(zhuǎn)換。如template <int *a>class A{};int b[1];A<b>m。

Const修飾符的轉(zhuǎn)換。如template <const int *a>class A{};int b;A<&b>m;即從int * 到const int *的轉(zhuǎn)換。

提升轉(zhuǎn)換。如template <int a>class A{};const shor b;A<b>m;即從short到int的提升轉(zhuǎn)換。

整值轉(zhuǎn)換。如template <unsigned int a> class A{};A<3> m;即從int到unsigned int的轉(zhuǎn)換。

可以為類模板的類型形參提供默認(rèn)值,但不能為函數(shù)模板的類型形參提供默認(rèn)值。函數(shù)模板和類模板都可以為模板的非類型形參提供默認(rèn)值。如template <class T1,class T2=int>class A{};為第二個(gè)模板類型形參提供int型的默認(rèn)值。

類模板的類型形參默認(rèn)值和函數(shù)的默認(rèn)參數(shù)一樣,如果有多個(gè)類型形參則從***個(gè)設(shè)定了默認(rèn)值之后所以的模板形參都應(yīng)設(shè)定默認(rèn)值。如template <class T1=int,class T2>class D{};就是錯(cuò)誤的,因?yàn)闆]有給T2設(shè)定默認(rèn)值。但在外部定義類中的成員時(shí),應(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(){};

現(xiàn)將以上小節(jié)總結(jié)于以下一例,并通過vs2010調(diào)試,請(qǐng)讀者仔細(xì)相關(guān)知識(shí)點(diǎn)的應(yīng)用。

#include <iostream> using namespace std; //模板的聲明或定義只能在全局,命名空間或類范圍內(nèi)進(jìn)行。即不能在局部范圍,函數(shù)內(nèi)進(jìn)行,比如不能在main函數(shù)中聲明或定義一個(gè)模板。 //類模板的定義 template<class T>class A{public:T g(T a, T b); A();};  //定義帶有一個(gè)類模板類型形參T的類A template<class T1,class T2>class B{public:void g();}; //定義帶有兩個(gè)類模板類型形參T1,T2的類B //定義類模板的默認(rèn)類型形參,默認(rèn)類型形參不適合于函數(shù)模板。 template<class T1,class T2=int> class D{public: void g();}; //定義帶默認(rèn)類型形參的類模板。這里把T2默認(rèn)設(shè)置為int型。 //template<class T1=int, class T2>class E{}; //錯(cuò)誤,為T1設(shè)了默認(rèn)類型形參則T1后面的所有形參都必須設(shè)置認(rèn)默值。 //以下為非類型形參的定義 //非類型形參只能是整型,指針和引用,像double,String, String **這樣的類型是不允許的。但是double &,double *對(duì)象的引用或指針是正確的。 template<class T1,int a> class Ci{public:void g();}; //定義模板的非類型形參,形參為整型 template<class T1,int &a>class Cip{public:void g();};  template<class T1,A<int>* m> class Cc{public:void g();}; //定義模板的模板類型形參,形參為int型的類A的對(duì)象的指針。 template<class T1,double*a>class Cd{public:void g();};  //定義模板的非類型形參,形參為double類型的引用。 class E{}; template<class T1,E &m> class Ce{}; //非類型模板形參為對(duì)象的引用。 //以下非類型形參的聲明是錯(cuò)誤的。 //template<class T1,A m>class Cc{}; //錯(cuò)誤,對(duì)象不能做為非類型形參,非類型模板形參的類型只能是對(duì)象的引用或指針。 //template<class T1,double a>class Cc{}; //錯(cuò)誤,非類型模板的形參不能是double類型,可以是double的引用。 //template<class T1,A<int> m>class Cc{}; //錯(cuò)誤,非類型模板的形參不能是對(duì)象,必須是對(duì)象的引用或指針。這條規(guī)則對(duì)于模板型參也不例外。 //在類模板外部定義各種類成員的方法, //typeid(變量名).name()的作用是提取變量名的類型,如int a,則cout<<typeid(a).name()將輸出int template<class T>   A<T>::A(){cout<<"class A goucao"<<typeid(T).name()<<endl;} //在類模板外部定義類的構(gòu)造函數(shù)的方法 template<class T> T A<T>::g(T a,T b){cout<<"class A g(T a,T b)"<<endl;} //在類模板外部定義類模板的成員 template<class T1,class T2>  void B<T1,T2>::g(){cout<<"class g f()"<<typeid(T1).name()<<typeid(T2).name()<<endl;} //在類外面定義類的成員時(shí)template后面的模板形參應(yīng)與要定義的類的模板形參一致 template<class T1,int a>     void Ci<T1,a>::g(){cout<<"class Ci g()"<<typeid(T1).name()<<endl;} template<class T1,int &a>    void Cip<T1,a>::g(){cout<<"class Cip g()"<<typeid(T1).name()<<endl;}  //在類外部定義類的成員時(shí),template后的模板形參應(yīng)與要定義的類的模板形參一致 template<class T1,A<int> *m> void Cc<T1,m>::g(){cout<<"class Cc g()"<<typeid(T1).name()<<endl;} template<class T1,double* a> void Cd<T1,a>::g(){cout<<"class Cd g()"<<typeid(T1).name()<<endl;} //帶有默認(rèn)類型形參的模板類,在類的外部定義成員的方法。 //在類外部定義類的成員時(shí),template的形參表中默認(rèn)值應(yīng)省略 template<class T1,class T2>  void D<T1,T2>::g(){cout<<"class D g()"<<endl;} //template<class T1,class T2=int> void D<T1,T2>::g(){cout<<"class D k()"<<endl;} //錯(cuò)誤,在類模板外部定義帶有默認(rèn)類型的形參時(shí),在template的形參表中默認(rèn)值應(yīng)省略。 //定義一些全局變量。 int e=2;  double ed=2.2; double*pe=&ed; A<int> mw; A<int> *pec=&mw; E me; //main函數(shù)開始 int main() { // template<class T>void h(){} //錯(cuò)誤,模板的聲明或定義只能在全局,命名空間或類范圍內(nèi)進(jìn)行。即不能在局部范圍,函數(shù)內(nèi)進(jìn)行。     //A<2> m; //錯(cuò)誤,對(duì)類模板不存在實(shí)參推演問題,類模板必須在尖括號(hào)中明確指出其類型。     //類模板調(diào)用實(shí)例     A<int> ma; //輸出"class A goucao int"創(chuàng)建int型的類模板A的對(duì)象ma。     B<int,int> mb; mb.g(); //輸出"class B g() int int"創(chuàng)建類模板B的對(duì)象mb,并把類型形參T1和T2設(shè)計(jì)為int     //非類型形參的調(diào)用     //調(diào)用非類型模板形參的實(shí)參必須是一個(gè)常量表達(dá)式,即他必須能在編譯時(shí)計(jì)算出結(jié)果。任何局部對(duì)象,局部變量,局部對(duì)象的地址,局部 //    變量的地址都不是一個(gè)常量表達(dá)式,都不能用作非類型模板形參的實(shí)參。全局指針類型,全局變量,全局對(duì)象也不是一個(gè)常量表達(dá)式,不能 //        用作非類型模板形參的實(shí)參。         //全局變量的地址或引用,全局對(duì)象的地址或引用const類型變量是常量表達(dá)式,可以用作非類型模板形參的實(shí)參。         //調(diào)用整型int型非類型形參的方法為名為Ci,聲明形式為template<class T1,int a> class Ci        Ci<int,GHIJKLMJKLNOPQMII//正確,數(shù)值R是一個(gè)int型常量,輸出"class Ci g() int"         const int a2=3;Ci<int,a2> mci1; mci1.g(); //正確,因?yàn)閍2在這里是const型的常量。輸出"class Ci g() int"     //Ci<int,a> mci; //錯(cuò)誤,int型變量a是局部變量,不是一個(gè)常量表達(dá)式。     //Ci<int,e> mci; //錯(cuò)誤,全局int型變量e也不是一個(gè)常量表達(dá)式。     //調(diào)用int&型非類型形參的方法類名為Cip,聲明形式為template<class T1,int &a>class Cip     Cip<int,e> mcip;  //正確,對(duì)全局變量的引用或地址是常量表達(dá)式。     //Cip<int,a> mcip1; //錯(cuò)誤,局部變量的引用或地址不是常量表達(dá)式。     //調(diào)用double*類型的非類形形參類名為Cd,聲明形式為template<class T1,double *a>class Cd     Cd<int,&ed> mcd; //正確,全局變量的引用或地址是常量表達(dá)式。     //Cd<int,pe> mcd1; //錯(cuò)誤,全局變量指針不是常量表達(dá)式。     //double dd=aNGMIITbULcdefbbHIJKbgMIhh錯(cuò)誤,局部變量的地址不是常量表達(dá)式,不能用作非類型形參的實(shí)參     //Cd<int,&e> mcd;  //錯(cuò)誤,非類型形參雖允許一些轉(zhuǎn)換,但這個(gè)轉(zhuǎn)換不能實(shí)現(xiàn)。     //調(diào)用模板類型形參對(duì)象A<int> *的方法類名為Cc,聲名形式為template<class T1,A<int>* m> class Cc     Cc<int,&mw> mcc; mcc.g(); //正確,全局對(duì)象的地址或者引用是常量表達(dá)式     //Cc<int,&ma> mcc;  //錯(cuò)誤,局部變量的地址或引用不是常量表達(dá)式。     //Cc<int,pec> mcc2;  //錯(cuò)誤,全局對(duì)象的指針不是常量表達(dá)式。     //調(diào)用非類型形參E&對(duì)象的引用的方法類名為Ce。聲明形式為template<class T1,E &m> class Ce     E me1; //Ce<int,me1> mce1; //錯(cuò)誤,局部對(duì)象不是常量表達(dá)式     Ce<int,me> mce;  //正確,全局對(duì)象的指針或引用是常量表達(dá)式。     //非類型形參的轉(zhuǎn)換示例,類名為Ci     //非類型形參允許從數(shù)組到指針,從函數(shù)到指針的轉(zhuǎn)換,const修飾符的轉(zhuǎn)換,提升轉(zhuǎn)換,整值轉(zhuǎn)換,常規(guī)轉(zhuǎn)換。     const short s=3 ;Ci<int,s> mci ;//正確,雖然short型和int不完全匹配,但這里可以將short型轉(zhuǎn)換為int型     return 0; }

與函數(shù)模板相同,類模板的聲明語句也必須至于類聲明的前面。有兩個(gè)以上模板參數(shù)時(shí),應(yīng)使用逗號(hào)分開。使用含類模板的類定義對(duì)象時(shí)也必須在類名的后面帶上“﹤實(shí)際類型﹥”的參數(shù)列表。類模板最常用于各種類包容關(guān)系的設(shè)計(jì)模型中。定義:

Template ﹤類型參數(shù)表﹥ class 類名 {類聲明體}

在使用類模板時(shí),應(yīng)注意以下幾點(diǎn):

在所有出現(xiàn)類模板的地方不能直接用類名表示,都應(yīng)加上﹤&hellip;﹥

在類模板定義體中,可以省略﹤&hellip;﹥

一個(gè)類模板的各個(gè)實(shí)例之間沒有特殊的聯(lián)系(形成一個(gè)獨(dú)立的類)如:Queue<int> qi 和Queue<string> qs,分別表示整數(shù)隊(duì)列和字符隊(duì)列

實(shí)例化時(shí)機(jī):在需要時(shí)實(shí)例化,比如定義指針或引用是不需要實(shí)例化,定義具體的變量或常量是會(huì)實(shí)例化,而訪問對(duì)象的成員時(shí)會(huì)實(shí)例化。如 Queue<int> *q //不實(shí)例化Queue<> ,Queue<int> iq //實(shí)例化Queue<>,iq->add(2) //實(shí)例化Queue<>

類模板中的友元

非模板函數(shù)、類成為所有實(shí)例類的友元。如:

Class Foo { void bar();}; Template <class Type> Class QueueItem {          Friend class Foobar;  //類Foobar不需要先定義或聲明,并沒有<>          Frined void foo();    //函數(shù)foo()          Frined void Foo::bar();//類Foo必須先定義 }

模板函數(shù)、模板類成為同類型實(shí)例類的友元。如:

Template <class Type> class Foo {&hellip;}; Template <class Type> void foo(QueueItem<Type>); Template <class Type> class Queue{ void bar();}; Template <class Type> class QueueItem {          Friend class Foo<Type>;  //模板類Foo需要先定義或聲明,并帶有<>          Friend void foo(QueueItem<Type>); //模板函數(shù)foo()需要先定義或聲明          Friend void Queue<Type>::bar();   //模板類Queue必須先定義 }

模板函數(shù)、模板類成為不同類型實(shí)例類的友元。如:

Template <class T> class QueueItem {          Template <class Type> friend class Foo;          Template <class Type> friend void foo(QueueItem<Type>);          Template <class Type> friend void Queue::bar(); }

類模板的顯示實(shí)例化:和函數(shù)模板的顯示實(shí)例化一樣都是以template 開始。比如template class A<int,int>;將類A顯示實(shí)例化為兩個(gè)int 型的類模板。這里要注意顯示實(shí)例化后面不能有對(duì)象名,且以分號(hào)結(jié)束。顯示實(shí)例化可以讓程序員控制模板實(shí)例化發(fā)生的時(shí)間。

1、類模板中有普通友元函數(shù),友元類,模板友元函數(shù)和友元類。

2、可以建立兩種類模板的友元模板,即約束型的友元模板和非約束型的友元模板。

3、非約束型友元模板:即類模板的友元模板類或者友元模板函數(shù)的任一實(shí)例都是外圍類的任一實(shí)例的友元,也就是外圍類和友元模板類或友元模板函數(shù)之間是多對(duì)多的關(guān)系

4、約束型友元模板:即類模板的友元模板類或友元模板函數(shù)的一個(gè)特定實(shí)例只是外圍類的相關(guān)的一個(gè)實(shí)例的友元。即外圍類和友元模板類或友元模板函數(shù)之間是一對(duì)一的關(guān)系。

5、約束型友元模板函數(shù)或友元類的建立:比如有前向聲明:template<class T1> void g(T1 a); template<class T2> void g1(); template<class T3>class B;則template<class T>class A{friend void g<>(T a); friend void g1<T>(); friend class B<T>;};就建立了三個(gè)約束型友元模板,其中g(shù)和g1是函數(shù),而B是類。注意其中的語法。這里g<int>型和類A<int>型是一對(duì)一的友元關(guān)系,g<double>和A<double>是一個(gè)一對(duì)一的友元關(guān)系。

6、非約束型友元模板函數(shù)或友元類的建立:非約束型友元模板和外圍類具有不同的模板形參,比如template<class T>class A{template<class T1> friend void g(T1 a); template<class T2> friend class B;}注意其中的語法,非約束型友元模板都要以template開頭。要注意友元模板類,在類名B的后面沒有尖括號(hào)。

7、不存在部分約束型的友元模板或者友元類:比如template<class T> class A{template<class T1>friend void g(T1 a, T b);

template<class T3>friend class B<T3,T>;}其中函數(shù)g具有template<class T1,class T2>void g(T1 a,T2 b)的形式。其中的函數(shù)g試圖把第二個(gè)模板形參部分約束為類A的模板形參類型,但是這是無笑的,這種語法的結(jié)果是g函數(shù)的非約束型類友元函數(shù),而對(duì)類B的友元聲明則是一種語法錯(cuò)誤。

類模板中的模板成員(模板函數(shù),模板類)和靜態(tài)成員

1、類模板中的模板函數(shù)和模板類的聲明:與普通模板的聲明方式相同,即都是以template 開始

2、在類模板外定義類模板中的模板成員的方法:比如template<class  T1> class A {public:template<class T2> class B;

template<class T3> void g(T3 a);};則在類模板外定義模板成員的方法為,template<class  T1> template<class T2> class A<T1>::B{};定義模板函數(shù)的方法為:template<class T1>

template<class T3> void A<T1>::g(T3 a){}其中***個(gè)template指明外圍類的模板形參,第二個(gè)template指定模板成員的模板形參,而作用域解析運(yùn)算符指明是來自哪個(gè)類的成員。

3、實(shí)例化類模板的模板成員函數(shù):比如上例中要實(shí)例化函數(shù)g()則方法為, A<int> m; m.g(2);這里外圍類A的模板形參由尖括號(hào)中指出,而類中的模板函數(shù)的參數(shù)由整型值2推演出為int 型。

4、創(chuàng)建類模板中的模板成員類的對(duì)象的方法:比如上例中要?jiǎng)?chuàng)建模板成員類B的方法為,A<int>::B<int> m1;A<int>::B<doble>m2;  A<double>::B<int> m3;在類模板成員B的前面要使用作用域解析運(yùn)算符以指定來自哪個(gè)外圍類,并且在尖括號(hào)中要指定創(chuàng)建哪個(gè)外圍類的實(shí)例的對(duì)象。這里說明在類模板中定義模板類成員時(shí)就意味意該外圍模板類的一個(gè)實(shí)例比如int 實(shí)例將包含有多個(gè)模板成員類的實(shí)例。比如這里類A的int 實(shí)例就有兩個(gè)模板成員類B的int 和double兩個(gè)實(shí)例版本。

5、要訪問類模板中的模板成員類的成員遵守嵌套類的規(guī)則,因?yàn)轭惸0逯械哪0宄蓡T類就是一個(gè)嵌套類。即外圍類和嵌套類中的成員是相互獨(dú)立的,要訪問其中的成員只能通過嵌套類的指針,引用或?qū)ο蟮姆绞絹碓L問。具體情況見嵌套類部分。

6、類模板中的靜態(tài)成員是類模板的所有實(shí)例所共享的。

#include <iostream> using namespace std; template<class T1,class T2> class A{public:int a,b; static int e;}; template<class T3,class T4> class B; template<class T5,class T6> void g(T5 a,T6 b); class C{public:void gc(){cout<<"class C gc()"<<endl;}}; void g1(){cout<<"putong g1()"<<endl;} } template<class T1,class T2>template<class T3,class T4}class A<T1,T2>::B{public:void gb(){cout<<"moban class B gb()"<<endl;}} //在類模板外面定義類模板的模板成員類的方法 template<class T1,class T2>template<class T5,class T6}void A<T1,T2>::g(T5 a,T6 b){cout<<"moban g()"<<endl;}//在類模板外面定義類模板的模板成員函數(shù)的方法 template<class T1,class T2> int A<T1,T2>::e=0;//在類模板外面定義靜態(tài)成員的方法。 int main() {     A<int,int> ma;     ma.g(2,3);//創(chuàng)建模板類中模板成員函數(shù)的方法,在這里模板類A的模板形參被設(shè)為int,而模板成員函數(shù)的模板形參則由兩個(gè)int型的整    數(shù)推演為int型。     ma.e=1; A<int,int>::e=2;  //把類模板A的int,int型實(shí)例的靜態(tài)成員設(shè)為。     cout<<"ma.e="<<ma.e<<endl;      A<int,double> ma1;     cout<<"ma.e="<<ma1.e<<A<int,int>::e<<A<int,double>::e<<endl; //因?yàn)轭惸0錋的int,int型實(shí)例和int,double實(shí)例是兩個(gè)實(shí)例,所以這里的靜態(tài)常量e的值不是三個(gè)二。     A<int,int>::B<int,int> mb;  //聲明模板類中模板成員類的方法。     mb.gb();//調(diào)用嵌套類B的成員函數(shù)     //mb.g(); //錯(cuò)誤,函數(shù)g()是外圍類的成員,嵌套類不能訪問外圍類的成員     return 0; }

簡單模板實(shí)例,參數(shù)列表為基本類型:

#include<iostream.h> template<class T> class Array {    T *ar; public:         Array(int c){ar=new T[c];} void init(int n,T x){ar[n]=x;    }     T& operator[](int n){return ar[n];} }; void main() {      Array<int> array(5);     cout<<"Please input every element's value:"<<endl;     for(int i=0;i<5;i++)     {          cout<<"No."<<i+1<<':';           cin>>array[i];       } }

類模板參數(shù)是類:

#include<iostream.h> class A {        int j; public:     A(){}     A(int x):j(x){}     A(A *x){j=x->j;}     void operator!(){cout<<"J="<<j<<endl;} };  template<class T> class B {       int i;      T *x; public:     B(int xa,T *p):i(xa){x=new T(p);}     void operator!(){cout<<"I="<<i<<endl;!*x;} };   void main() {     A a(1);        //***的顯示結(jié)果為:     B<A> b(2,&a);  //I=2     !b;           //J=1 }

Typename的使用:

#include <iostream> template<class T> class X  {      typename T::id i; //如無typename看看情況如何 public: void f()         {             i.g();         } };    class Y  {  public: class id          {              public: void g()                      {                         std::cout<<"Hello World!"<<std::endl;                     }          };  };    int main()  {      //Y y;     X<Y> xy;      xy.f();      return 0; }

Typename關(guān)鍵字告訴編譯器把一個(gè)特殊的名字解釋成一個(gè)類型,在下列情況下必須對(duì)一個(gè)name使用typename關(guān)鍵字:

一個(gè)唯一的name(可以作為類型理解),嵌套在另一個(gè)類型中。

依賴于一個(gè)模板參數(shù),就是說,模板參數(shù)在某種程度上包含這個(gè)name。當(dāng)模板參數(shù)使編譯器在指認(rèn)一個(gè)類型時(shí)便會(huì)產(chǎn)生誤解。

在定義模板時(shí),typename和class作用基本相同,至于二者的其他關(guān)系沒有什么區(qū)別,僅是歷史原因,typename僅是一個(gè)新生代。

復(fù)雜的模板類實(shí)例:

#include<iostream.h> #include<string.h> class Student {     int number;     static Student *ip;     Student *p; public:     Student(){p=NULL;}     Student(int n);     static Student* get_first(){return ip;}     int get_number(){return this->number;}     Student* get_next(){return this->p;} };   Student::Student(int n):number(n)  //依據(jù)學(xué)號(hào)的大小順序?qū)W(xué)生對(duì)象插入鏈表 {     p=NULL;     if(ip==NULL)ip=this;  //如果是***個(gè)則使頭指針指向該對(duì)象     else{Student *temp=ip;           if(n<ip->number){ip=this;p=temp;}//如學(xué)號(hào)小于***個(gè)學(xué)生對(duì)象的學(xué)號(hào)則使頭指針指向該對(duì)象     else {         while(temp)         {                          if(n<temp->p->number)             {                 p=temp->p;  //鏈中間對(duì)象的插入                 temp->p=this;                 break;             }else              {                     if(temp->p->p==NULL)  //***一個(gè)鏈的插入                 {                     temp->p->p=this;break;                 }                    }             temp=temp->p;         }     }     } } Student* Student::ip=NULL; template<class T> class Class {     int num;     T *p; public:        Class(){}        Class(int n):num(n){p=NULL;}        T* insert(int n){p=new T(n);return p;}        void list_all_member(T* x)        {   T *temp=x;        while(temp) { cout<<temp->get_number()<<",";temp=temp->get_next();}        } }; void main() {      Class<Student> x97x(9707);     x97x.insert(23);     x97x.insert(12);     x97x.insert(38);     x97x.insert(22);     x97x.insert(32);     x97x.list_all_member(Student::get_first()); }

現(xiàn)在來討論模板安全:模板根據(jù)參數(shù)的類型進(jìn)行實(shí)例化。因?yàn)橥ǔJ孪炔恢榔渚唧w類型,所以也無法確切知道將在哪兒產(chǎn)生異常。程序員需要知道程序在什么地方發(fā)生了異常。下面看一個(gè)簡單的模板類:

Template <typename T> Class Wrapper { Public:          Wrapper(){}          T get(){return value_;}          T set(T const &value){value_=value;} Private:          T value_;          Wrapper(Wrapper const &);         Wrapper &operator=(Wrapper const &); };

實(shí)例化過程很簡單,如Wrapper <int> i;因?yàn)閃rapper <int>只接受int或其引用,所以不會(huì)觸及異常,Wrapper <int>不拋異常,也沒有直接或者間接調(diào)用任何可能拋異常的函數(shù),因此Wrapper <int>是異常安全的。

現(xiàn)在再來看Wrapper<X>x,這里X是一個(gè)類。在這個(gè)定義里,編譯器實(shí)例化了:

Template <> Class Wrapper<X> { Public:          Wrapper(){}          X get(){return value_;}          X set(X const &value){value_=value;} Private:          T value_;          Wrapper(Wrapper const &);         Wrapper &operator=(Wrapper const &); };

現(xiàn)在就有問題出現(xiàn)了:

Wrapper<X> 包含了一個(gè)X的子對(duì)象。這個(gè)子對(duì)象需要構(gòu)造,意味著調(diào)用X的構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)可能拋出異常。

Wrapper<X>::get()產(chǎn)生并返回了一個(gè)X的臨時(shí)對(duì)象。為了這個(gè)臨時(shí)對(duì)象,get()調(diào)用了X的拷貝構(gòu)造函數(shù),這個(gè)函數(shù)可能拋出異常。

Wrapper<X>::set()執(zhí)行了表達(dá)式value_=value,他實(shí)際上調(diào)用了X的賦值運(yùn)算。這個(gè)運(yùn)算可能拋出異常。

可以看到,同樣的模板和同樣的語句,但其含義不同。由于這樣的不確定性,我們需要采用保守策略:假設(shè)Wrapper會(huì)根據(jù)類來實(shí)例化,而這些類在其成員上沒有進(jìn)行異常規(guī)格申明,則他們可能拋出異常。

再假設(shè)Wrapper的異常規(guī)格申明承諾其成員不產(chǎn)生異常。至少必須在其成員上加上異常規(guī)格申明throw(),所以需要修補(bǔ)掉這些可能導(dǎo)致異常的地方:

在Wrapper::Wrapper()中構(gòu)造value_的過程。

在Wrapper::get()中返回value_的過程。

在Wrapper::set()中隊(duì)value_的賦值過程。

另外,在違背throw()的異常規(guī)格申明是,還要處理std::unexpected.

再來看默認(rèn)構(gòu)造函數(shù):

Wrapper() throw() Try:T() {} Catch (&hellip;){}

雖然看上去不錯(cuò),但它不能工作,根據(jù)C++標(biāo)準(zhǔn):對(duì)構(gòu)造函數(shù)或析構(gòu)函數(shù)上的function-try-block,當(dāng)控制權(quán)到達(dá)了異常處理函數(shù)的結(jié)束點(diǎn)是,被捕獲的異常被再次拋出。對(duì)于一般的函數(shù),此時(shí)是函數(shù)返回,等同于沒有返回值的return 語句,對(duì)于定了返回類型的函數(shù)此時(shí)的行為未定義。換句話說,上面的程序相當(dāng)于:

X::X() throw() Try: T (){} Catch (&hellip;){ throw;}

這不是程序本來想要的結(jié)果,換成以下代碼:

X::X() throw() Try: T (){} Catch (&hellip;){ return;}

但是它卻違背了標(biāo)準(zhǔn):如果在構(gòu)造函數(shù)上的function-try-block的異常處理函數(shù)體中出現(xiàn)了return語句,則程序是病態(tài)的。最終:無法用function-try-block快來實(shí)現(xiàn)構(gòu)造函數(shù)的接口安全。

引申原則1:盡可能使用構(gòu)造函數(shù)不拋異常的基類或成員子對(duì)象。

引申原則2:為了幫助別人實(shí)現(xiàn)原則1,不要從構(gòu)造函數(shù)中拋出任何異常。

其他方面的不再討論,比如析構(gòu)與關(guān)鍵字new等。總之,良好的設(shè)計(jì)必須滿足以下兩個(gè)原則:

通過異常對(duì)象的存在來注視異常狀態(tài),并適當(dāng)?shù)淖龀龇磻?yīng)。

確保創(chuàng)造和傳播異常對(duì)象不會(huì)造成更大的破壞。

最終代碼的參考將如下:

template <typename T> class wrapper { public:     wrapper()     throw() : value_(NULL)     {               try          {                 value_ = new T;         }         catch (...) {      }     }     ~wrapper() throw()     {             try          {              delete value_;         }         catch (...){operator delete(value_);}     }     bool get(T &value) const throw(){return assign(value, *value_);}     bool set(T const &value) throw(){return assign(*value_, value);} private:     bool assign(T &to, T const &from) throw()     {         bool error(false);         try{to = from; }         catch (...) { error = true;}         return error;     }     T *value_;     wrapper(wrapper const &);     wrapper &operator=(wrapper const &); }; void main() {     wrapper<int> mywrapper(); }

可以像使用普通類的方法來使用模板類,這一點(diǎn)毫無疑問,例如:可以繼承、創(chuàng)建一個(gè)從現(xiàn)有模板繼承過來的并已經(jīng)初始化的模板?,F(xiàn)在,我們來看看模板的繼承,如果vector已經(jīng)為你做了很多事,但你還想加入sort()的功能,則可用下面代碼來擴(kuò)充。

#ifndef SORTED #define SORTED  #include<vector>   template <class T> class Sorted:public std::vector<T> { public:         void sort(); };   template <class T> void Sorted<T>::sort() {     for (int i=size();i>0;i--)     {         for (int j=1;j<i;j++)         {             if (at(j-1)>at(j))             {                 T t=at(j-1);                 at(j-1)=at(j);                 at(j)=t;             }         }     } }   #endif

實(shí)現(xiàn)文件

#include "123.h"  #include <iostream>  #include <string>  using namespace std;  char* words[] = {"is", "running", "big", "dog", "a"};  char* words2[] = { "this", "that", "theother" };  int main()  {      Sorted<int> is;      for(int i = 15; i >0; i--)    is.push_back(i);      for(int l = 0; l < is.size(); l++)  cout << is[l] << ' ';      cout << endl;      is.sort();      for(l = 0; l < is.size(); l++) cout << is[l] << ' ';      cout << endl;      Sorted<string*> ss;      for(i = 0; i < 5; i++) ss.push_back(new string(words[i]));      for(i = 0; i < ss.size(); i++) cout << *ss[i] << ' ';      cout << endl;      ss.sort();      for(i = 0; i < ss.size(); i++) cout << *ss[i] << ' ';      cout << endl;      Sorted<char*> scp;      for(i = 0; i < 3; i++) scp.push_back(words2[i]);      for(i = 0; i < scp.size(); i++) cout << scp[i] << ' ';      cout << endl;      scp.sort();      for(i = 0; i < scp.size(); i++) cout << scp[i] << ' ';      cout << endl;      return  0; }

以上簡單實(shí)現(xiàn)了模板的繼承,讀者可自行編寫相關(guān)代碼進(jìn)行測試,并分析模板繼承情況下,析構(gòu)函數(shù)和構(gòu)造函數(shù)等的消壞和初始化情況,這里不這討論。

注意:子類并不會(huì)從通用的模板基類繼承而來,只能是從基類的某一個(gè)實(shí)例繼承而來。

現(xiàn)將模板的繼承方式總結(jié)以下幾點(diǎn):

基類是模板類的一個(gè)特定實(shí)例化的版本。比如:template <class T1> class B:public A<int>{}.

基類是一個(gè)和子類相關(guān)的一個(gè)實(shí)例。比如:template <class T1>class B:public A<T1>{}。這時(shí)實(shí)例化基類就相應(yīng)的被實(shí)例化一個(gè)和基類相同的實(shí)例版本,比如:B<int> b;模板B被實(shí)例化為int 版本,這時(shí)基類A也相應(yīng)的被實(shí)例化為Int版本。

如果基類是一個(gè)特定的實(shí)例化版本,這時(shí)子類可以不是一個(gè)模板,比如:class B:public A<int>{};。

每次實(shí)例化一個(gè)模板,模板的代碼都會(huì)被重新生成(除了inline標(biāo)記的函數(shù)),如果一個(gè)模板某些函數(shù)不依賴于特定的類型參數(shù)而存在,那它們就可以放置在一個(gè)通用的基礎(chǔ)類中,來阻止無意義的代碼重生。

Inline函數(shù)因不產(chǎn)生新的代碼所以它們是自由的,在整個(gè)過程中,功能性的代碼只是在我們創(chuàng)建基礎(chǔ)類代碼時(shí)產(chǎn)生了一次,而且,所屬權(quán)的問題也因?yàn)樵黾恿诵碌奈鰳?gòu)函數(shù)而解決。通常模板只有在需要的時(shí)候才實(shí)例化,對(duì)函數(shù)模板來說,這就意味著調(diào)用它時(shí)才被實(shí)例化,但對(duì)類模板來說,它更加明細(xì)化,只有在使用到模板中的某個(gè)函數(shù)式,函數(shù)才會(huì)被實(shí)例化,換句話說:只有用到的成員函數(shù)被實(shí)例化了。例如:

#include <iostream.h> class X  { public:      void x()      {           cout<<"This is fuction x()"<<endl;     }  };  class Y  { public:      void y()      {            cout<<"This is fuction y()"<<endl;      }  };    template <typename T> class Z  {     T t;  public:      void a() { t.x(); }      void b() { t.y(); }  };    int main()  {     Z<X> zx;      zx.a(); // Doesn't create Z<X>::b()      Z<Y> zy;      zy.b(); // Doesn't create Z<Y>::a()     return 0; }

***用模板技術(shù)演示list的的使用。

// Template class for storing list elements #include <iostream> #include <string> using namespace std; template <class T>                          // Use template keyword class ListElement                            //定義類ListElement,用于表示list對(duì)象 { public:     T data;     ListElement<T> * next ;     ListElement(T& i_d, ListElement <T>* i_n)     : data(i_d),next(i_n) { }     ListElement<T>* copy()                    // copy includes all next elements     {             return new ListElement(data,(next?next->copy():0));         } }; template <class T>  class ListIterator        //定義類ListIterator,用于訪問和操作list對(duì)象 {  public :                                             //ListIterator(List<T>& l) ;    T operator()() ;    int operator++() ;    int operator!() ; public:      ListElement<T>* rep ; }; template <class T> T ListIterator<T>::operator() () {      if (rep) return rep->data;     else      {         T tmp ;    return tmp ;                // Default value       } } template <class T> int ListIterator<T>::operator++() {        if (rep)         rep = rep->next ;        return (rep != 0) ; } template <class T> int ListIterator<T>::operator!() {        return (rep != 0) ; } template <class T> class List                //定義類List  { public:     friend class ListIterator<T> ;     List();     List(const List&);     ~List();     List<T>& operator=(const List<T>&);                                             // typical list ops     T head();     List<T> tail();     void add(T&);     friend ostream& operator<<(ostream&, const List<T>&); public:     void clear() ;                            // Delete all list elements     ListElement<T>* rep ; };                                             // Default Constructor template <class T> List<T>::List(){ rep = 0 ; }                                             // Copy Constructor template <class T>List<T>::List(const List<T>& l) {          rep = l.rep ? l.rep->copy() : 0 ; }                                             // Overloaded assignment operator template <class T> List<T>& List<T>::operator=(const List<T>& l) {     if (rep != l.rep)        {                 clear() ;                 rep = l.rep ? l.rep->copy() : 0 ;         }     return *this ; }                                             // Destructor template <class T> List<T>::~List(){  clear() ;}                                             // Delete representation template <class T> void List<T>::clear() {   while (rep)     {   ListElement<T>* tmp = rep ;         rep = rep->next ;         delete tmp ;     }     rep = 0 ; }                                             // Add element to front of list template <class T> void List<T>::add(T& i) {        rep = new ListElement<T>(i,rep) ; }                                             // Return head of list or default value of type T template <class T> T List<T>::head() {      if (rep) return rep->data ;      else      {         T tmp ;          return tmp ;     } }                                             // Return tail of list or empty list template <class T> List<T> List<T>::tail() {    List<T> tmp ;      if (rep)        if (rep->next) tmp.rep = rep->next->copy() ;      return tmp ; }                                             // Output operator template <class T> ostream& operator<<(ostream& os, const List<T>& l) {     if (l.rep)     {         ListElement<T>* p = l.rep ;         os << "( " ;         while (p){ os << p->data << " " ;   p = p->next ; }         os << ")\n" ;     }     else     os << "Empty list\n" ;     return os ; } int main() {    List<int> l ;                            // Integer list    cout << l ;    int i=1;    l.add(i) ;    i=2;    l.add(i) ;    i=3;    l.add(i) ;    cout << "l is " << l << endl ;    cout << "head of l is " << l.head() << endl ;    List<int> m = l.tail() ;    List<int> o ;    o = m;    i=4;    m.add(i);    cout << "m is " << m << endl ;    cout << "o is " << o << endl ;    List<char> clist ;                        // Character list    char ch;    ch='a';    clist.add(ch);    ch='b';    clist.add(ch);    cout << clist << endl ;    List<string> ls ;                        // string List    ls.add(string("hello")) ;    ls.add(string("world")) ;    cout << "List of strings" << endl ;    cout << ls << endl ;    List<List<int> > listlist ;                // List of lists of integer. Notice that lists of lists are possible    listlist.add(o) ;    listlist.add(m) ;    cout << "List of lists of ints\n" ;    cout << listlist << endl ;    List<List<List<int> > > lllist ;            // List of lists of lists of integer    lllist.add(listlist) ;    lllist.add(listlist) ;    cout << "List of lists of lists of ints\n" ;    cout << lllist << "\n" ;    List<List<string> > slist ;               // List of list of strings    slist.add(ls) ;    slist.add(ls) ;    cout << "List of lists of strings\n" ;    cout << slist << "\n" ;    return 0 ; }

注意:以上某一個(gè)實(shí)例好像未通過調(diào)試,基于時(shí)間本人已忘記,讀者發(fā)現(xiàn)后可查看相關(guān)情況自行更正。

“C++函數(shù)模板和類模板分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI