第8章模板概述:C++语言中的模板是进行模块化程序开发和大型软件开发的有力工具,为编程带来了极大的便利。本章将讨论模板的概念,分析模板与运算符重载的区别、函数模板与模板函数的区别以及类模板与模板类的区别,并通过具体的例题说明它们的应用。8.1模板的概念在前面的章节中讨论了函数名的重载问题,由于使用函数重载技术,使编程显得更为简捷有效,且可以对不同类型的数据进行处理,但在函数名重载中,有一点不便的是必须逐一对所要处理的数据类型进行重载,若某一数据类型不在重载函数之内,就不能使用重载函数。例题----采用函数重载分别为对实数和整数求最大值floatmax(floata,floatb){return(ab?a:b);}intmax(inta,intb){return(ab?a:b);}使用函数重载的缺陷在定义了重载函数后,对实数和整数类型的数据可以用同一个函数max完成,但对新的数据类型必须重新定义,例如,对char数据类型,就无法调用上面的函数。事实上,在软件的开发过程中,数据的类型是多样的,且是不断发展变化的,为了使编程更为有效,更具有模块化结构,可采用模板技术。应用模板,可将类型参数化,只建立一个抽象的形式,具体作用于什么类型到应用时再决定。8.2函数模板与模板函数函数模板的一般声明形式是:template类型形式参数表返回类型函数名(形式参数表){//函数体}其中,template为声明模板的关键字,类型形式参数表可以为C++语言中的基本数据类型,也可以为类类型,若是类类型,必须加前缀class,以表明是类类型。说明上面仅是给出了一个函数模板的一般定义,而不是一个具体的函数,编译系统不为其产生任何执行代码,只有当编译系统在程序的编译过程中发现了一个具体的函数调用时,才根据具体的参数类型,生成相应的代码,此时的代码被称为模板函数,即由函数模板生成的具体的一个函数。所以,函数模板只是模板函数的一个抽象定义,不涉及到具体的数据类型,而模板函数是函数模板的一个具体实例,涉及到参数中的具体类型。附加说明重载函数就相当于一个函数模板的显式模板函数,而定义函数模板的目的就是为了不显式地定义模板函数,而由编译系统隐式地完成这样的目的。例函数模板的应用--------两个数之间的极大值问题//定义函数模板#includeiostream.htemplateclassTTmax(Ta,Tb){returnab?a:b;}//文件main.cpp,应用程序voidmain(){inta1=5,a2=7;floatb1=5.5,b2=10.1;doublec1=10.001,c2=20.0002;chard1=‘x’,d2=‘y’;coutMax(a1,a2)=max(al,a2)endl;coutMax(b1,b2)=max(bl,b2)endl;coutMax(c1,c2)=max(cl,c2)endl;coutMax(d1,d2)=max(dl,d2)endl;}程序运行结果为:Max(5,7)=7Max(5.5,10.1)=10.1Max(10.001,20.0002)=20.0002Max(x,y)=y解释在上述例题中,定义了一个函数模板max,其形式参数为类类型T,在函数模板的函数体中,有对两个参数的比较运算,这就要求在调用函数时,必须保证实参数的类型具有这样的比较运算,才能使函数的调用有意义。本例中函数摸板被调用的类型有整数(int)、实型(float)、双精度型(double)和字符型(char),它们是可以进行大小比较运算的。函数模板与模板函数的关系例题表明,函数模板的使用要比定义重载函数显得更为有效便利,它为具有不同的数据类型,而且具有相同处理过程的函数提供了抽象的公共模板,极大地提高了编程的效益。函数模板是模板函数的一个抽象定义,模板函数是函数模板的具体实例。例函数模板的应用二打印数组元素//定义函数模板#includeiostream.htemplateclassTvoidprintArray(T*array,constintcount){for(inti=0;icount;i++)coutarray[i],;//输出数组元素coutendl;}//文件main.cpp,主应用程序voidmain(){constintaCount=5,bCount=7,cCount=6;inta[aCount]={1,2,3,4,5};//定义整数数组//定义实数数组floatb[bCount]={1.1,2.2,3.3,4.4,5.5,6.6,7.7};charc[cCount]=HELLO;//定义字符数组coutArrayacontains:endl;printArray(a,aCount);//整数型的模板函数调用coutArraybcontains:endl;printArray(b,bCount);//实数型的模板函数调用coutArrayccontains:endl;printArray(c,cCount);//字符型的模板函数调用}程序运行结果为:Arrayacontains:1,2,3,4,5,Arraybcontains:1.1,2.2,3.3,4.4,5.5,6.6,7.7,ArrayCcontains:H,E,L,L,O,在例7.2中,定义了一个函数模板printArray,实现对一个长度为count的数组输出,函数模板的形式参数为类型T,至于具体是什么形式的数据类型,在函数调用时才决定。说明在主函数中分别定义了3个类型的数组:整数型数组、实数型数组和字符型数组,且分别调用了函数模板printArray,实现对各自数组内容的输出,因此,事实上,就产生了3个模板函数:printArray(a,aCount)printArray(b,bCount)printArray(c,cCount)。模板函数要注意以下问题(1)在模板函数的声明语句中间不能插入其他语句。如:templateclassTintj;//错误Tadd(Tx,Ty){returnx+y;}这样在编译时会出现错误。(2)“类型形式参数表”可以由多个参数类型组成,且每个类型由关键字class引导,如:templateclassT1,classT2voidadd(T1x,T1y,T2z){//函数体}(3)函数模板的异常处理问题。在应用函数模板生成模板函数时,必须遵守参数类型一致型原则,若出现不一致情况,可采用强制类型转换等手段。如在前面的例题中,主函数的程序可写成:voidmain(){inta1=5,a2=7;floatb1=5.5,b2=10.1;doublec1=10.001,c2=20.0002;coutMax(a1,a2)=max(a1,a2)endl;coutMax(b1,b2)=max(b1,b2)endl;coutMax(c1,c2)=max(c1,c2)endl;coutMax(‘x’,’y’)=max(‘x’,‘y’)endl;//coutmax(a1,b1);//错误,可以改成如下强制类型转换coutMax(a1,b1)=max(a1,int(b1))endl;}8.3类模板与模板类类模板的一般定义形式是:template类型形式参数表class类名{//类声明体}template类型形式参数表返回类型类名类型名表::成员函数名1(形式参数表){//成员函数定义体}……template类型形式参数表返回类型类名类型名表::成员函数名n(形式参数表){//成员函数定义体}其中,类型形式参数表(或称为模板形参表)与函数模板中的类型形式参数表意义一样。类模板体的定义和一般类体定义相同建立了类模板后,就可以用下面方法创建模板类:类名类型实参数表对象名;“对象名”就是应用类模板产生的模板类,它们的关系就像函数模板与模板函数一样,类模板只是模板类的一个抽象定义,不涉及具体的数据类型,而模板类是类模板的一个具体实例,涉及到参数中的具体类型。例类模板的应用:栈。在本例中,定义了一个栈的类模板Stack,对栈有两个典型的操作push和pop,在类模板中也进行了相应的定义,另有两个成员函数isFull()和isEmpty(),用来判别Stack是否已满或已空。当Stack满时,就不能进行push操作,而当Stack空时,同样不能进行pop操作,所以,这两个成员函数将对Stack的两个特殊情况进行判断,以便程序正常进行,由于它们仅仅是对Stack的状态的反映,而不对类的数据成员进行修改,故被声明为常数型成员函数。//文件stack.h,定义类模板Stack#includeiostream.htemplateclassTclassStack{public:Stack(int=10);//构造函数,缺省长度为10~Stack(){delete[]stackPtr;}//释放内存intpush(constT&);//将元素压入栈intpop(T&);//将元素取出栈intisEmpty()const{returntop==-1;}//判断栈是否空intisFull()const{returntop==size-1;}//判断栈是否满private:intsize,top;T*stackPtr;};templateclassTStackT::Stack(ints){size=s0&&s1000?s:10;//top=-1;stackPtr=newT[size];}//pushanelementontothestacktemplateclassTintStackT::push(constT&item){if(!isFull()){stackPtr[++top]=item;return1;}return0;}//popanelementoffthestacktemplateclassTintStackT::pop(constT&popValue){if(!isEmpty()){popValue=stackPtr[top--];return1;}return0;}//文件main.cpp,主应用函数#includeiostream.h#includestack.hvoidmain(){StackfloatfloatStack(5);//创建实数型栈floatf=1.1;coutPushingelementsontofloatStackendl;while(floatStack.push(f)){coutf;f+=1.1;}coutendlStackisfull.CannotpushfendlendlPopingelementsfromfloatStackendl;while(floatStack.pop(f))coutf;coutendlStackisempty.Cannotpopendl;StackintintStack;//创建整数型栈intj=1;coutPushingelementsontointStackendl;while(intStack.push(j)){coutj;j+=1;}coutendlStackisfull.CannotpushjendlendlPopingelementsfromintStackendl;while(intStack.pop(j))coutj;coutendlStackisempty.Cannotpopendl;