C++类模板函数模板与模板函数_第1页
C++类模板函数模板与模板函数_第2页
C++类模板函数模板与模板函数_第3页
C++类模板函数模板与模板函数_第4页
C++类模板函数模板与模板函数_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第六章

模板

6.1模板的概念

C++是强类型语言,因此我们定义求最大值函数max()时,需要对不同的数据类型分别定义不同的版本,例如:intmax(intx,inty){return(x>y)?x:y;}floatmax(floatx,floaty){return(x>y)?x:y;}doublemax(doublex,doubley){return(x>y)?x:y;}这些函数版本执行的功能都是相同的,只是参数类型和返回类型不同,能否为上述这些函数只写出一套代码?解决问题的一个方法就是使用模板。所谓模板,就是写一个函数模子,用这个模子套印出许多功能相同,参数类型和返回类型不同的函数。模板是实现了真正的代码可重用性。可以这么说:函数重载是指用同一个名字定义不同的函数,这些函数功能不同,执行不同的操作。函数模板是指用同一个名字定义不同的函数,这些函数功能相同,而参数类型和返回类型不同。1模板模板分为函数模板〔模子〕和类模板〔模子〕,允许用户分别用它们构造〔套印〕出〔模板〕函数和〔模板〕类。图显示了模板〔函数模板和类模板〕,模板函数,模板类和对象之间的关系。

模板(函数模板和类模板)模板函数模板类对象实例化实例化实例化模子26.2函数模板与模板函数6.2.1函数模板的声明与模板函数的生成函数模板的声明格式如下:template<class类型参数>返回类型函数名〔模板形参表〕{函数体}其中template是一个声明模板的关键字,它表示声明一个模板。例如,将求最大值函数max()定义成函数模板,如下所示:template<classT>或template<typenameT>Tmax(Tx,Ty){return(x>y)?x:y;}其中T为类型参数,它可用根本类型或用户自定义的类型。在使用函数模板时,必须将其实例化,即用实际的数据类型替代它。3例6.1函数模板的程序#include<iostream.h>#include<string.h>template<classAT>ATmax(ATx,ATy){return(x>y)?X:y;}voidmain(){intil=10,i2=56;floatfl=12.5,f2=24.5;doubleb1=50.344,d2=4656.346;charc1=’k’,c2=’n’;cout<<”themaxofil,i2is:“<<max(i1,i2)<<endl;cout<<”themaxoffl,f2is:“<<max(f1,f2)<<endl;cout<<”themaxofdl,d2is:“<<max(d1,d2)<<endl;cout<<”themaxofcl,c2is:“<<max(c1,c2)<<endl;}程序运行结果如下:themaxofil,i2is:56themaxoffl,f2is:24.5themaxofdl,d2is:4656.346themaxofcl,c2is:n4函数模板和模板函数的关系

例6.2与指针有关的模板#include<iostream.h>template<classT>Tsum(T*array,intsize=0){Ttotal=0;for(inti=0;i<size;i++)total+=array[i];returntotal;};函数模板max(x,y)模板函数max(c1,c2)(c1,c2为字符型)实例化5模板函数max(f1,f2)(f1,f2为浮点型)模板函数max(d1,d2)(d1,d2为双精型)模板函数max(i1,i2)(i1,i2为整型)实例化实例化实例化接1例6.2intint_array[]={1,2,3,4,5,6,7,8,9,10};doubledouble_array[]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10};voidmain(){intitotal=sum(int_array,10);doubledtotal=sum(double_array,10);cout<<”Thesummaryofintegerarrayare:”<<itotal<<endl;cout<<”Thesummaryofdoublearrayare:”<<dtotal<<endl;}程序运行结果为:Thesummaryofintegerarrayare:55Thesummaryofdoublearrayare:59.6几点说明:⑴在函数模板中允许使用多个类型参数。但在template定义局部的每个模板形参前必须有关键字class。6几点说明:例6.3#include<iostream.h>template<classtype1,classtype2>voidmyfunc(type1x,type2y){cout<<x<<’’<<y<<end1;}voidmain(){myfunc(10,”hao”);myfunc(0.123,10L);}程序运行结果为:10hao0.123107⑵在template语句与函数模板定义语句之间不允许有别的语句。//这是不能编译的Template<classT>intiI;//错误,不允许有别的语句Tmax(Tx,Ty){return(x>y)?x:y;}⑶模板函数类似于重载函数,只不过它更严格一些而已。函数被重载的时候,在每个函数体内可以执行不同的动作,但同一函数模板实例化后的所有模板函数都必须执行相同的动作。//不能做函数模板的voidoutdate(inti){cout<<i;}voidoutdata(doubled){cout<<”d=”<<d<<endl;}86.2.2函数模板的异常处理虽然函数模板中的模板形参T可以实例化为各种类型,但实例化T的各模板实参之间必须保持完全一致的类型,否那么会发生错误。请看下面的例子。Template<classT>Tmax(Tx,Ty){return(x>y)?x:y;}voidfun(inti,charc){max(i,i);//正确,调用max(int,int)max(c,c);//正确,调用max(char,char)max(i,c);//错误max(c,i);//错误}这里出现错误的原因是,如对语句max(i,c);编译器将先按变量i将T解释为int类型,此后出现的模板实参c不能解释为int类型时,便发生错误。解决这个问题有以下两种方法:⑴采用强制类型转换,如将调用语句max(i,c);改写成max(i,int(c));9⑵用非模板函数重载函数模板,这种重载有两种表述方式:①只声明一个非模板函数的原型,而不给出函数体,它的函数体是借用函数模板的函数体。当执行此重载版本时会自动调用函数模板的函数体。template<classT>Tmax(Tx,Ty){return(x>y)?x:y;}intmax(int,int);//非模板函数的原型voidfun(inti,charc){max(i,i);//正确,调用max(int,int)max(c,c);//正确,调用max(char,char)max(i,c);//正确,调用max(int,int),它支持数据间的隐式转换max(c,i);//正确,调用max(int,int),它支持数据间的隐式转换}②定义一个完整的非模板函数,此方法定义的重载函数,所带参数的类型可以随意,就像一般的重载函数一样定义。例如:char*max(char*x,chen*y){return(strcmp(x,y>0)?x:y;)}10当出现调用语句max(“abcd”,”efgh”);时,执行的是这个重载的非模板函数。在c++中函数模板与同名的非模板函数重载时,调用的顺序遵循下述约定⑴寻找一个参数完全匹配的函数,如果找到了就调用它。⑵寻找一个函数模板,将其实例化,产生一个匹配的模板函数,假设找到了,就调用它。⑶假设⑴和⑵都失败,再试一试低一级的对函数的重载方法,假设找到了,就调用它。11

6.3类模板和模板类一个类模板(也称为类属类或类生成类)允许用户为类定义一种模子,使得实例化类中的某些数据成员,某些成员函数的参数或者返回值,能取任意数据类型。定义一个类模板,其格式如下:template<classType>class类名{//…};关键字class〔或typename〕后面的Type是类型参数。在实例化类定义中,欲采用通用数据类型的数据成员,成员函数的参数或返回值,前面需要加上Type。例如,下面的程序中建立了一个用来实现堆栈的类模板。12constintsize=10Template<classType>classstack{Typestck[size];Inttos;public:voidinit(){tos=0;}voidpush(Typech);Typepop();};成员函数push()和pop()在类定义体外定义为template<classType>voidstack<Type>::push(Typeob){if(tos==size){cout<<”stackisfull”;return;}stck[tos]=ob;tos++;}13template<classType>Typestack<Type>::pop(){if(tos==0){cout<,”stackisempty”;return0;}tos--;returnstck[tos];}类模板不代表一个具体的、实际的类,而代表一类类。实际上,类模板的使用就是将类模板实例化成一个具体的类,它的格式为:类名<实际的类型>对象名;例如,使用上面的类模板,创立两个模板参数为char型的对象,语句如下:stack<char>s1,s2;14类模板stack(Type)模板类stack(char)模板类stack(int)模板类stack(double)实例化实例化实例化15例6.4类模板stack的例子,在此建立了字符型和整型两个堆栈。#include<iostream.h>constintsize=10;template<classType>//声明一个类模板classstack{//定义类模板Typestck[size];//数组可取任意类型,即模板参数类型TypeInttos;public:voidinit(){tos=0;}voidpush(Typech);//参数取Type类型Typepop();//返回类型取Type类型};16template<classType>voidstack<Type>::push(Typeob){if(tos==size){cout<<”stackisfull”;return;}stck[tos]=ob;tos++;}template<classType>Typestack<Type>::pop(){if(tos==0){cout<<”stackisempty”;return0;}tos--;returnstck[tos];}17接1例6.4voidmain(){//定义字符堆栈stack<char>s1,s2;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;i<3;i++)cout<<”pops1:”<<s1.pop()<<endl;for(i=0;i<3;i++)cout<<”pops2:”<<s2.pop()<<endl;

18//定义整型堆栈stack<int>is1,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;i<3;i++)cout<<”popis1:”<<is1.pop()<<endl;for(i=0;i<3;i++)cout<<”popis2:”<<is2.pop()<<endl;}程序运行结果如下:pops1:cpops2:zpopis1:5popis2:6pops1:bpops2:ypopis1:3popis2:4pops1:apops2:xpopis1:1popis2:219例6.5建立一个单向链表类模板,然后建立一个保存字符的链表类。#include<iostream.h>template<classdata_t>classlist{data_tdata;list*next;public:list(data_td);voidadd(list*node){node->next=this;next=0;}list*getnext(){returnnext;}data_tgetdata(){returndata;}};template<classdata_t>list<data_t>::list(data_td){data=d;next=0;}20voidmain(){list<char>start(‘a’);list<char>*p,*last;inti;//buildalistlast=&start;for(i=1;i<26;i++){p=newlist<char>(‘a’+i);p->add(last);last=p;}cout<<endl;//followthelistp=&start;while(p){cout<<p->getdata();p=p->getnext();}}程序运行结果如下:abcdefghijklmnopqrstuvwxyz21说明:(1)在每个类模板定义之前,都需要在前面加上模板声明template<classType>类模板在使用时,必须在名字后面缀上模板参数<Type>stack<Type>(2)模板类可以有多个模板参数,在下面的例中建立了使用两个模板参数的类模板。例6.6使用两个模板参数的类模板#include<iostream.h>template<classT1,classT2>//声明具有两个参数的模板classmyclass{//定义类模板T1i;T2j;public:myclass(T1a,T2b){i=a;j=b;}voidshow(){cout<<”I=”<<i<<”j=”<<j<<endl;}};22voidmain(){myclass<int,double>od1(12,0.15);myclass<char,char*>ob2(‘x’,”Thisisatest”);od1.show();ob2.show();}程序运行结果如下:I=12j=0.15I=xj=Thisisatest236.4应用举例例6.7template<classT>structquenode{Tnodedata;quenode*next;};这里用模板的形式定义了队列链表的一个结点的构造,struct也可改为class。也用模板的形式定义了队列类,其中4个数据成员,10个函数成员:template<classT>classqueue{protected://保护段①intquesize;//定义队列长度②quenode<T>*head;//定义队列列头③quenode<T>*tail;//定义队列列尾④boolallocateerror;

24T类型的数据quenode类型的指针nodedatanext4个数据成员quesizebool类型整数指向列头的指针指向列尾的指针headtailallocateerror①

queue©(queue&q);//队列拷贝函数public:②

queue();//构造函数③queue(queue&q){head=NULL;tail=NULL;copy(q);}//构造函数,建q队列④~queue(){clearque();}//析构函数⑤boolgetallocateerror(){returnallocateerror;}⑥voidpush(T&);//将结点插入队尾⑦boolpop(T&);//从队头取结点⑧boolisempty(){return(quesize==0)?true:false;}//判断队列是否为空⑨voidclearque();//清空队列⑩queue&operator=(queue&q){copy(q);return*this;}//重载赋值运算符};其中:黑色的函数名③④⑤⑧⑩已在类内完成定义,红色的函数名①②⑥⑦⑨以模板函数的形式在下面定义:25②template<classT>queue<T>::queue()//定义构造函数{quesize=0;allocateerror=false;head=NULL;tail=NULL;}①template<classT>queue<T>&queue<T>::copy(queue<T>&que)

//将队列que复制给当前队列对象{quenode<T>*p,*q,*r;if(head)clearque();quesize=que.quesize;//传递队列长度allocateerror=false;head=NULL;tail=NULL;

26if(!que.head)return*this;//假设队列为空,那么返回head=newquenode<T>;//为队列头结点分配存储空间if(!head)//假设分配失败,那么返回{allocateerror=true;return*this;}head->nodedata=que.head->nodedata;//将que队列头结点值赋给当前队列头结点head->next=NULL;tail=head;//将队列尾也指向此结点r=NULL;p=head;//p指针也指向此结点q=que.head->next;//q指针指向que队列的第二个结点

27

while(q){r=newquenode<T>;//为结点r分配存储空间if(!r){allocateerror=true;return*this;}r->nodedata=q->nodedata;r->next=NULL;p->next=r;//将结点r链接到当前队列的链上tail=r;//队尾指针指向r,因为r为最后一个结点p=p->next;//指针后移q=q->next;}return*this;}28⑥template<classT>voidqueue<T>::push(T&x)//向队尾插入结点{quenode<T>*p;p=newquenode<T>;//为p结点分配存储空间if(!p)//假设分配失败,那么返回{allocateerror=true;return;}p->nodedata=x;if(tail)//假设队列非空{p->next=NULL;tail->next=p;//将p结点链接到尾指针tail后tail=p;//修改队尾指针}else//假设队列为空{p->next=NULL;tail=p;//p结点为队列的头,又为尾head=p;}quesize++;//长度加1}29⑦template<classT>boolqueue<T>::pop(T&x)//从队头取一结点{quenode<T>*p;if(head)//假设队列非空{x=head->nodedata;//将队头的数据内容赋给xp=head;head=head->next;//修改队头指针if(head==NULL)//假设队列已删空,那么tail也改为NULLtail=NULL;deletep;//删除原头结点quesize--;//长度减1returntrue;}returnfalse;}30

⑨template<classT>voidqueue<T>::clearque()//将队列清空{Tp;allocateerror=false;while(pop(p));//循环提取队

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论