您好,登錄后才能下訂單哦!
本篇內(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 ,…﹥返回類型 函數(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•一"<<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)加上﹤…﹥
在類模板定義體中,可以省略﹤…﹥
一個(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 {…}; 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 (…){}
雖然看上去不錯(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 (…){ throw;}
這不是程序本來想要的結(jié)果,換成以下代碼:
X::X() throw() Try: T (){} Catch (…){ 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í)用文章!
免責(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)容。