第8章模板模板的引入intmax(intx,inty){return(xy)?x:y;}floatmax(floatx,floaty){return(xy)?x:y;}doublemax(doublex,doubley){return(xy)?x:y;}宏定义:#definemax(x,y)((xy)?x:y)模板8.1模板的概念模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码重用。模板分为函数模板和类模板,它们分别允许用户构造模板函数和模板类。模板(函数模板和类模板)模板函数模板类对象实例化实例化实例化8.2函数模板与模板函数8.2.1函数模板的说明函数模板的一般说明形式如下:templateclass类型参数返回类型函数名(模板形参表){函数体}其中,template是一个声明模板的关键字,它表示声明一个模板。例如,将求最大值函数max()定义成函数模板,如下所示:templateclassTTmax(Tx,Ty){return(xy)?x:y;}也可以定义成如下形式:templatetypenameTTmax(Tx,Ty){return(xy)?x:y;}其中,T为类型参数,它既可取系统预定义的数据类型,又可以取用户自定义的类型。8.2.2函数模板的使用将T实例化的参数称为模板实参,用模板实参实例化的函数称为模板函数。当编译系统发现有一个函数调用:函数名(模板实参表);将根据模板实参表中的类型生成一个函数即模板函数。该模板函数的函数体与函数模板的函数定义体相同。例8.1函数模板的使用。#includeiostream.htemplateclassTTmax(Tx,Ty){return(xy)?x:y;}main(){inti1=10,i2=56;floatf1=12.5,f2=24.5;doubled1=50.344,d2=4656.346;charc1='k',c2='n';coutThemaxofi1,i2is:max(i1,i2)endl;coutThemaxoff1,f2is:max(f1,f2)endl;coutThemaxofd1,d2is:max(d1,d2)endl;coutThemaxofc1,c2is:max(c1,c2)endl;return0;}例8.2有关指针的函数模板#includeiostream.htemplateclassATATsum(AT*array,intsize=0){ATtotal=0;for(inti=0;isize;i++)total+=array(i);returntotal;}int_array=[......];double_array=[......];voidmain(){intitotal=sum(int_array,10);doubledtotal=sum(double_array,10);coutitotalendl;coutdtotalendl;}说明1.在template语句与函数模板定义语句之间不能有别的语句templateclassTinti;//错误Tmax(Tx,Ty){return(xy)?x:y;}2.模板函数类似于重载函数,只不过它更严格一些.在函数重载时,每个函数体可以执行不同的动作.但同一函数模板实例化后的所有模板函数都必须执行相同的动作.3.在函数模板中允许使用多个类型参数,每个类型参数前必须有关键字class/typename.例8.3有两个类型参数的函数模板。#includeiostream.htemplatetypenametype1,typenametype2voidmyfunc(type1x,type2y){coutx““yendl;}main(){myfunc(10,hao);myfunc(0.123,10L);return0;}8.2.3用户定义的参数类型用户可以在函数模板形参表和对模板函数的调用中使用类类型和其他自定义的类型。如果这样,就必须对模板函数中对类对象产生作用的基本运算符进行重载。例8.4#includeiostream.hclassnumber{public:number(intx1,inty1){x=x1;y=y1;}intgetx(){returnx;}intgety(){returny;}intoperator(number&c);private:intx,y;};intnumber::operator(number&c){if(x+yc.x+c.y)return1;return0;}templateclassobjobj&max(obj&o1,obj&o2){if(o1o2)returno1;returno2;}voidmain(){inti1=5,i2=55;cout较大的数:max(i1,i2)endl;numberc1(5,11);numberc2(6,23);numberc3=max(c1,c2);cout较大的和:c3.getx()+c3.gety()endl;}8.2.4函数模板的异常处理templateclassTTmax(Tx,Ty){return(xy)?x:y;}voidfun(inti,charc){max(i,i);max(c,c);max(i,c);}max(i,int(c));用非模板函数重载函数模板,有两种表述方式:1.只声明一个非模板函数原型,而不给出函数体.该非模板函数是借用函数模板的函数体.当执行此重载版本时会自动调用函数模板的函数体.templateclassTTmax(Tx,Ty){return(xy)?x:y;}intmax(int,int);voidfun(inti,charc){max(i,i);max(i,c);}用非模板函数重载函数模板,有两种表述方式:2.定义一个完整的非模板函数.此方式定义的重载函数,所带参数的类型可以随意,就像一般的重载函数一样.templateclassTTmax(Tx,Ty){return(xy)?x:y;}intmax(intx,chary){return(xy)?x:y;}voidfun(inti,charc){max(i,i);max(i,c);}在C++中,函数模板和同名的非模板函数重载时,调用的顺序遵循以下约定:1.寻找一个参数完全匹配的函数,如果有则调用之.2.寻找一个函数模板,将其实例化,产生一个匹配的模板函数,若匹配则调用之.3.若1步和2步都失败,就再试低一级的对函数调用方法,例如通过类型转换可产生参数匹配等.若找到了则调用之.4.若1步2步3步都未找到匹配的参数,则返回一个错误的调用.5.若在1步有多于一个的选择,即函数调用存在二义性,则也是错误的调用.8.3类模板与模板类类模板允许用户为类定义一种模式,使得类中的某些数据成员,某些成员函数的参数或返回值,能取任意数据类型。定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,然后是类名,其格式如下:templateclassTypeclass类名{//…};模板参数类型参数在类定义中,欲采用通用数据类型的数据成员,成员函数的参数或返回值前面需要加上Type.constintsize=10templateclassTypeclassstack{public:{tos=0;}voidpush(Typech);Typepop();private:Typestck(size);inttos;};在类体外定义有模板形参的成员函数时,需要在函数体外进行模板声明,并且在函数名前的类名后缀上“Type”templateclassTypevoidstackType::push(Typeob){if(tos==size){cout“stackisfull”;return;}stck[tos]=ob;tos++;}在类体外定义有模板形参的成员函数时,需要在函数体外进行模板声明,并且在函数名前的类名后缀上“Type”templateclassTypeTypestackType::pop(){if(tos==0){cout“stackisempty”;return0;}tos--;returnstck[tos];}类模板不是代表一个具体的、实际的类,而是代表着一类类。实际上,类模板的使用就是将类模板实例化成一个具体的类,它的格式为:类名实际的类型对象名;例:stackchars1,s2;例8.5使用栈类模板#includeiostream.hconstintsize=10;templateclassType//声明一个类模板classstack{public:voidinit(){tos=0;}voidpush(Typech);//参数取Type类型Typepop();//返回类型取Type类型private:Typestck[size];//数组的类型为类型参数Type,即可取任意类型inttos;};templateclassTypevoidstackType::push(Typeob){//......}templateclassTypeTypestackType::pop(){//......}main(){//定义字符堆栈stackchars1,s2;//创建两个模板参数为char型的对象inti;s1.init();s2.init();s1.push('a');s2.push('x');s1.push('b');s2.push('y');s1.push('c');s2.push('z');for(i=0;i3;i++)coutpops1:s1.pop()endl;for(i=0;i3;i++)coutpops2:s2.pop()endl;//定义整型堆栈stackintis1,is2;//创建两个模板参数为int型的对象is1.init();is2.init();is1.push(1);is2.push(2);is1.push(3);is2.push(4);is1.push(5);is2.push(6);for(i=0;i3;i++)coutpopis1:is1.pop()endl;for(i=0;i3;i++)coutpopis2:is2.pop()endl;return0;}类模板stackType模板类stackchar模板类stackint模板类stackdouble实例化实例化实例化例8.6链表类模板#includeiostream.htemplateclassdata_tclasslist{public:list(data_td);voidadd(list*node);{node-next=this;next=0;}list*getnext(){returnnext;}data_tgetdata(){returndata;}private:data_tdatalist*next;}templateclassdata_tlistdata_t::list(data_td){data=d;next=0;}main(){listcharstrat(‘a’);listchar*p,*last;inti;last=&start;for(i=1;i26;i++;){p=newlistchar(‘a’+i);p-add(last);last=p;}//......}说明1.在每个类模板定义之前,都需要在前面加上模板声明:templateclassTypeclassstack{......类模板在使用时,必须