1第二部分面向对象的程序设计第3章类和对象(一)第4章类和对象(二)第5章继承和派生第6章虚函数与多态性第7章运算符重载第8章模板第9章标准模板库STL第10章C++语言的输入和输出2第8章模板本章重点:模板的概念函数模板与模板函数类模板与模板类3模板是C++最重要的特性之一,使用模版可以设计出与数据类型无关的程序框架,可以建立具有通用类型的类库和函数库。模板是C++软件重用机制的又一完美体现,引出了参数化多态性的概念,即把程序所处理的对象的类型参数化,使得一段程序可以用于多种不同类型的对象。48.1模板的概念前面已经学过函数的重载,对于int型和float型数据,必须定义两个单独的函数Max(),才能实现对两个数求最大值的功能。如下程序所示的函数Max,,实现两个函数的主要操作都是一样的,唯一的差别是:一个函数处理int型数据,另一个函数处理float型数据。intMax(inta,intb)//求两个int数最大值{returnab?a:b;}floatMax(floata,floatb)//求两个float数最大值{returnab?a:b;}可以看出,求任何类型两个数的最大值,都有下列函数定义形式:TMax(Ta,Tb)//求两个T类型数最大值{returnab?a:b;}5这种Max重载函数的统一特征是参数类型不同,函数体操作却完全相同。考虑这样两个问题:第一,能否避免函数重载时的重复工作?第二,这些重载函数对新的数据类型是否支持?要解决这两个问题,C++中的模板就可以做到。有了模板,重复的函数重载工作可以省略;良好的模板再结合运算符重载等其他机制,也能够适用于各种新定义的数据类型。68.2函数模板与模板函数函数模板是函数的一种抽象形式。函数模板的定义形式为:templateclassT1,classT2,...返回类型函数模板名(数据参教表){函数模板的函数体}7前面的Max函数因此可以写成如下形式:templatetypenameTTMax(Ta,Tb)//求两个T类型数最大值{returnab?a:b;}8模板并不是函数,它是以具体的类型为实参来生成函数体的一种程序框架,C++在编译函数模板时,不会产生任何执行代码。只有在用函数模板来定义具体函数时,才会生成执行代码,而使用函数模板则只需以函数模板名为函数名进行函数调用即可。9【例8.1】编写求最大和求绝对值两个函数模板。/*08_01.cpp*/#includeiostreamusingnamespacestd;templatetypenameTTMax(Ta,Tb)//求两个T类型数最大值{returnab?a:b;}templatetypenameTTAbs(Ta)//求T类型数的绝对值{returna=0?a:-a;}10intmain(){intia=-5,ib=11,ic;floatfa=3.14f,fb=7.2f,fc;ic=Max(ia,ib);cout”Max(ia,ib)=”icendl;ic=Abs(ia);cout”Abs(ia)=”icendl;fc=Max(fa,fb);cout”Max(fa,fb)=”fcendl;fc=Abs(fa);cout”Abs(fa)=”fcendl;return0;}11Max(ia,ib)=11Abs(ia)=5Max(fa,fb)=7.2Abs(fa)=3.14程序的运行结果为:12C++的编译器在扫描到Max(ia,ib)时,因为是第一次发现Max函数调用,而ia,ib是整型,因此确定函数模板中的抽象类型T为int,所以内部生成如下的模板函数:templateintintMax(inta,intb){returnab?a:b;}而把此参数类型具体化产生新的模板函数的过程称为函数模板实例化。13【例8.2】重载模板函数。/*08_02.cpp*/#includeiostream.h#includestring.htemplatetypenameTTMax(Ta,Tb)//求两个T类型数最大值{returnab?a:b;}char*Max(char*pa,char*pb){returnstrcmp(pa,pb)0?pa:pb;}voidmain(){coutMax(10,20)endl;coutMax(Hello,Fellow)endl;}1420Hello程序的运行结果为:15【例8.3】模板函数与Student类的结合。/*08_03.cpp*/#includeiostream.h//使用该头文件,因为本程序将进行运算符重载#includestring.htemplatetypenameTT&Max(T&a,T&b)//使用引用类型来传递对象{returnab?a:b;}classStudent{intnumber;charname[20];16public:Student(inti,char*s)//构造学生对象{number=i;strcpy(name,s);}booloperator(Student&st)//重载运算符“”{returnnumberst.number;//返回成员number的比较结果}voidPrint()//输出结果{cout”Number:”numberendl;cout”Name:”nameendl;}};intmain(){Studentst1(12,”Li”),st2(13,”Zhang”),st3(0,””);st3=Max(st1,st2);st3.Print();return0;}17Number:13Name:Zhang程序的运行结果为:188.3类模板与模板类类模板就是设计一种类的框架,可以适用于不同的数据类型,是类的抽象。利用类模板可以针对不同的数据类型定义出具有共性的一组类。与函数模板相似,通过使用类模板可使得所定义的类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值都可以是任意类型的(包括基本类型和用户自定义类型)。也可以这样说,通过类模板可将程序所处理对象(数据)的类型参数化,从而使得同一段程序可用于处理多种不同类型的对象(数据),提高了程序的抽象层次与可重用性。19定义类模板的一般格式为:template类型形参列表class类模板名{类模板体定义};其中类型形参列表与函数模板形式相同,如typenameT1,typenameT2,…….20【例8.4】定义类模板SafeArray,用来表述安全数组的概念。/*08_04.cpp*/#includeiostreamusingnamespacestd;templatetypenameT,intsizeclassSafeArray//定义类模板{private:Tary[size];//数组ary大小由模板参数size决定public:SafeArray()//构造函数{for(inti=0;isize;i++)ary[i]=0;//初值设为0,对基本数据类型有效}21T&operator[](inti)//重载运算符[]{if(i0||i=size)//进行下标的安全检查,超出范围就退出程序{cout”Indexoutofbound!”endl;exit(0);}returnary[i];}voidPrint()//输出数组元素{for(inti=0;isize;i++)coutary[i];coutendl;}};22类模板的成员函数可以放在类模板的定义体中,与普通成员函数定义方法一样,也可以放在类模板的外部定义,类模板的成员函数其实都是函数模板,其定义形式如下:template类型形参列表函数返回类型类模板名类型名表::函数名(形参表){函数体}23类模板本身不是具体的类,其中涉及的数据类型都是抽象的类型,要想使用类模板,必需先用实际的类型来取代抽象的类型,此过程将确定类模板的实例,即模板类,然后再用该类定义对象,其格式如下:类模板名类型实参表列对象名;24【例8.5】重新定义类模板SafeArray,并进行测试。/*08_05.cpp*/#includeiostreamusingnamespacestd;templatetypenameT,intsizeclassSafeArray//定义类模板{private:Tary[size];//数组ary大小有模板参数size决定public:SafeArray();//构造函数T&operator[](inti);//重载运算符[]voidPrint();//输出数组元素};templatetypenameT,intsizeSafeArrayT,size::SafeArray()//构造函数{for(inti=0;isize;i++)ary[i]=0;//初值设为0,对基本数据类型有效}25templatetypenameT,intsizeT&SafeArrayT,size::operator[](inti)//重载运算符[]{if(i0||i=size)//进行下标的安全检查,超出范围就退出程序{cout”Indexoutofbound”endl;exit(0);}returnary[i];}templatetypenameT,intsizevoidSafeArrayT,size::Print()//输出数组元素{for(inti=0;isize;i++)coutary[i];coutendl;}26intmain(){SafeArrayint,5ais;SafeArrayfloat,4afs;ais[2]=10;//修改了对象ais的第2个元素afs[3]=3.1f;//修改了对象afs的第3个元素ais.Print();afs.Print();return0;}270003.1001000程序的运行结果为:28【例8.6】定义堆栈类模板Stack,实现堆栈基本操作,并测试。/*08_06.cpp*/#includeiostreamusingnamespacestd;templatetypenameTclassStack{private:T*pstk;//堆栈数据存储区指针变量intcapacity;//堆栈容量inttop;//栈顶指针计数器public:Stack(intnum);//构造函数boolPush(T&t);//入栈T*Pop();//出栈~Stack()//析构函数,释放动态生成的存储空间{delete[]pstk;}};29templatetypenameTStackT::Stack(intnum)//构造函数{pstk=newT[num];//根据形参动态生成的存储空间size=num;top=0;}templatetypenameTboolStackT::Push(T&t)//入栈操作{if(top=size)//判断是否栈已满{cout”Stackisfull.”endl;returnfalse;//返回操作失败}pstk[top++]=t;//元素赋值,栈顶指针加1returntrue;//返回成功}30templatetypenameTT*StackT::Pop()//出栈操作{if(top=0)//判断栈空{cout”Stackisempty.”endl;returnNULL;//返回空值表示栈空,操作失败}return&pstk[--top];//返回栈内元素地址,栈顶指针减1}intmain(){inta=10,b=12,*pa=NULL;Stackintstk;//定义用来存储整型数的堆