




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第9章模板及其应用
9.1函数模板 9.2类模板 9.3类模板应用
本章介绍函数模板与类模板的定义及其使用方法。通过使用模板,可使所编程序更加紧凑,增加程序的通用性及可重用性。
1
9.1函数模板9.1.1函数模板的概念及其定义格式9.1.2函数模板的应用举例----------------------------------
9.1.1函数模板的概念及其定义格式
--参看书p221,9.2.2小节
通常设计的算法(处理语句)是可以处理多种数据类型的,但目前处理相同的问题,仍要分别定义多个类似的函数。
2
int
max(inta,intb){ if(a>b) returna; else returnb;}
doublemax(doublea,doubleb){ if(a>b) returna; else returnb;}
charmax(chara,charb){ ...}
...
3
实际上,若“提取”出一个可变化的类型参数T,则可“综合”成为如下的同一个函数(模板),它实际上代表着一组函数:
Tmax(Ta,Tb){ if(a>b) returna; else returnb;}
在C++中定义完整的函数模板max时,格式如下:
template<classT>
Tmax(Ta,Tb){ if(a>b) returna; else returnb; }
4
函数模板定义的一般格式为:template<class类型形参名1,...,class类型形参名n>
返回类型函数模板名(形参表){函数体}注意:
1)应在“返回类型”或“形参表”或“函数体”中使用上述的“类型形参名”。
2)调用处则类似于一般函数,用户只需给出具体的实参。
3)模板函数调用时,不进行实参到形参类型的自动转换。
59.1.2函数模板的应用举例
--参看书p222-223,9.2.2小节部分1.函数模板例1
定义一个函数模板max,而后对它进行不同的调用。
#include<iostream.h>
template<classT>Tmax(Ta,Tb){if(a>b) returna;else returnb;}6
voidmain(){
inti1=-11,i2=0;doubled1,d2;
cout<<max(i1,i2)<<endl;
//由实参i1,i2,系统可确定“类型形参T”对应于intcout<<max(23,-56)<<endl;
cout<<max('f','k')<<endl;
cin>>d1>>d2;
cout<<max(d1,d2)<<endl;//cout<<"max(23,-5.6)="<<max(23,-5.6)<<endl;
//出错!不进行实参到形参类型的自动转换}7程序执行后的显示结果如下:inti1=-11,i2=0;=>max(i1,i2)=0max(23,-56)=23charc1='T',c2='F';=>max(c1,c2)=Tmax('f','k')=kinputdoubled1,d2:123.4599.67d1=123.45,d2=99.67=>max(d1,d2)=123.45
2.函数模板例2--函数模板与函数重载
定义一个函数模板与一个函数,它们都叫做min,C++允许这种函数模板与函数同名的所谓重载使用方法。但注意,在这种情况下,每当遇见函数调用时,C++编译器都将首先检查是否存在重载函数,若匹配成功则调用该函数,否则再去匹配函数模板。
8
#include<iostream.h>#include<string.h>
template<classtype>typemin(typea,typeb){//type型的a与b要能够进行“<”比较运算!
return(a<b?a:b);}
char*min(char*a,char*b){//函数min,字符串型参数,不能直接使用“<”来进行比较
return(strcmp(a,b)<0?a:b);}
voidmain(){
cout<<min(3,-10)<<endl; //使用函数模板
cout<<min(2.5,99.5)<<endl;
cout<<min('m','c')<<endl; char*str1="TheCprogram",*str2="TheC++program";
cout<<min(str1,str2)<<endl; //使用重载函数!}9
3.函数模板例3--二函数模板重载
定义两个函数模板,它们都叫做sum,都使用了一个类型参数Type,但两者的形参个数不同,C++允许使用这种函数模板重载的方法。注意,参数表中允许出现与类型形参Type无关的其它类型的参数,如“intsize”。
#include<iostream.h>template<classType>Typesum(Type*array,intsize){ //求array数组前size个元素之和
Typetotal=0;for(inti=0;i<size;i++)total+=*(array+i);returntotal;}10template<classType>Typesum(Type*a1,Type*a2,intsize){ //求a1数组与a2数组前size个元素之和
Typetotal=0;for(inti=0;i<size;i++)total+=a1[i]+a2[i];returntotal;}
voidmain(){
inta1[10],a2[8];floataf[10];... //为数组分量定值
cout<<sum(a1,10)<<endl;//求出a1数组前10个元素之和并输出
cout<<sum(af,10)<<endl;
cout<<sum(a1,a2,8)<<endl;//求a1与a2数组前8个元素之和并输出}
11
9.2类模板 9.2.1类模板的概念及其定义格式 9.2.2仅使用类型参数的类模板示例 9.2.3仅使用普通参数(非类型参数)的类模板示例 9.2.4既使用类型参数又使用普通参数的类模板示例
-------------------------
9.2.1类模板的概念及其定义格式
--参看书p219,9.2.1小节
利用类模板(带类型参数或普通参数的类),一次就可定义出具有共性的一组类。即,可使得所定义类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值都可以是任意类型的。
12
类模板定义格式如下:
template<类型形参或普通形参1的说明,...,类型形参或普通形参n的说明>class类模板名{带上述类型形参或普通形参名的类定义体}
说明类型形参时,使用“class类型形参名”的方式,说明普通形参时,使用“<类型>普通形参名”的方式。
注意:
1)类定义体中应使用上述的“类型形参名”及“普通形参名”。
2)
利用类模板说明类对象时,要随类模板名同时给出对应于类型形参或普通形参的具体实参(从而实例化为一个具体的类)。说明格式为:
类模板名<形参1的相应实参,...,形参n的相应实参>注意:类型形参的相应实参为类型名,而普通形参的相应实参必须为一个常量。13
3)
类模板的成员函数既可以在类体内进行说明(自动按内联函数处理),也可以在类体外进行说明。
在类体外说明(定义)时使用如下格式:
template<形参1的说明,...,形参n的说明>返回类型类模板名<形参1的名字,...,形参n的名字>::函数名(形参表){...//函数体};
上述的“形参1的名字”来自于“形参1的说明”,由“甩掉”说明部分的“类型”而得,是对类型形参或普通形参的使用。而“类模板名<形参1的名字,...,形参n的名字>::”所起的作用正是在类体外定义成员函数时在函数名前所加的类限定符!14
例如,对具有一个类型参数T的类模板TestClass,在类体外定义其成员函数getData时的大致样式如下:
template<classT>TTestClass<T>::getData(形参表){...//函数体};
其中的“TestClass<T>::”所起的作用正是在类体外定义成员函数时在函数名前所加的类限定符!
159.2.2仅使用类型参数的类模板示例#include<iostream.h>template<classT>classTestClass{public:Tbuffer[10];
//T类型的数据成员buffer数组大小固定为10(灵活性差!)
TgetData(intj);//获取T类型buffer(数组)的第j个分量
};
template<classT> TTestClass<T>::getData(intj){return*(buffer+j);};16
voidmain(){
TestClass<char>ClassInstA;
//char取代T,从而实例化为一个具体的类
charcArr[6]="abcde";
for(inti=0;i<5;i++)
ClassInstA.buffer[i]=cArr[i];
for(i=0;i<5;i++){ charres=ClassInstA.getData(i);
cout<<res<<""; }
cout<<endl;17
TestClass<double>ClassInstF;//实例化为另外一个具体的类
doublefArr[6]={12.1,23.2,34.3,45.4,56.5,67.6};
for(i=0;i<6;i++)
ClassInstF.buffer[i]=fArr[i]-10;
for(i=0;i<6;i++){ doubleres=ClassInstF.getData(i);
cout<<res<<""; }
cout<<endl;}
程序执行后的显示结果如下:abcde35.446.557.6189.2.3仅使用普通参数(非类型参数)的类模板示例#include<iostream.h>template<inti>classTestClass{public:
intbuffer[i];
//使buffer的大小可变化,但其类型则固定为int(灵活性差!)
intgetData(intj);};
template<inti> intTestClass<i>::getData(intj){return*(buffer+j);};19voidmain(){
TestClass<6>ClassInstF; doublefArr[6]={12.1,23.2,34.3,45.4,56.5,67.6};
for(i=0;i<6;i++)
ClassInstF.buffer[i]=fArr[i]-10;
for(i=0;i<6;i++){ doubleres=ClassInstF.getData(i);
cout<<res<<""; }
cout<<endl;}
程序执行后的显示结果如下:2132435465720
9.2.4既使用类型参数又使用
普通参数的类模板示例#include<iostream.h>#include"string.h"template<classT,inti>classTestClass{public:Tbuffer[i];
//T类型的buffer,其大小随普通形参i的值变化(灵活性大!)
TgetData(intj);};
template<classT,inti>TTestClass<T,i>::getData(intj){return*(buffer+j);};
21voidmain(){
TestClass<char,5>ClassInstA; charcArr[6]="abcde";
strcpy(ClassInstA.buffer,cArr);
for(inti=0;i<5;i++){ charres=ClassInstA.getData(i);
cout<<res<<""; }
cout<<endl;22
TestClass<double,6>ClassInstF; doublefArr[6]={12.1,23.2,34.3,45.4,56.5,67.6}; for(i=0;i<6;i++)
ClassInstF.buffer[i]=fArr[i]-10; for(i=0;i<6;i++){ doubleres=ClassInstF.getData(i);
cout<<res<<""; }
cout<<endl;}
程序执行后的显示结果如下:abcde35.446.557.623
9.3类模板应用 9.3.1设计一个队列类模板 9.3.2设计一个链表类模板 9.3.3stack类模板中可变类型T型数据的显示问题(特例函数) 9.3.4按不同方法派生出的类模板
249.3.1设计一个队列类模板
--参看书p216-218,9.1节
队列与栈不同,对数据采用“先进先出”--FIFO的管理方式(而栈则使用“先进后出”--FILO方式)。
队列数据放于作为类成员的动态数组queue之中,在构造函数中,将通过new来生成该动态数组,动态数组queue的大小由类的私有数据成员Maxsize之值来确定。但注意,此示例并没有循环使用上述的动态数组queue空间。即是说,队列中至多可以存放Maxsize个数据项,即使取走若干项后有了空闲空间后也不可重新进行使用。若稍加改造,使存取数据时首先通过对下标进行模Maxsize的运算,则可实现循环使用动态数组queue空间的功能,我们把它留作一个练习。
25
#include<iostream.h>#include<process.h>template<classkeytype>classQueue{
intMaxsize; //队列的大小
intfront,rear;//元素放在queue[front+1]到queue[rear]之中
keytype*queue; //动态数组queue,用来存放队列数据
public: Queue(intsize){//构造函数,生成动态数组来存放队列数据
Maxsize=size; queue=newkeytype[Maxsize]; front=rear=-1;//意味着队列为空 };26
intIsFull(){ if(rear==Maxsize-1) return1; else return0; };
intIsEmpty(){ if(front==rear) return1; else return0; };
voidAdd(constkeytype&);
keytypeDelete(void);};
27//Delete在类体外定义,函数名前要加类限定符“Queue<keytype>::”template<classkeytype>keytypeQueue<keytype>::Delete(void){if(IsEmpty()){
cout<<"thequeueisempty"<<endl;exit(0);}returnqueue[++front];}//Add在类体外定义template<classkeytype>voidQueue<keytype>::Add(constkeytype&item){if(IsFull())
cout<<"thequeueisfull"<<endl;elsequeue[++rear]=item;};28voidmain(){
inti=0;Queue<int>Qi(10);Queue<double>Qf1(10),Qf2(10);while(!Qi.IsFull()){//Qi中只能盛10个数
Qi.Add(2*i++);//Qi中:0,2,4,6,8,10,12,14,16,18
Qf1.Add(3.0*i);//Qf1中:3,6,9,12,15,18,21,24,27,30}for(i=0;i<4;i++){//四次循环,每次总//先往Qf2的队列尾部加入两个数,而后又从首部删取一个数并输出
Qf2.Add(4.5*Qi.Delete());//从Qi首删取一元素,乘以4.5,而后将其加入到Qf2尾部//四次循环往Qf2队列尾加入:0*4.5,2*4.5,4*4.5,6*4.5 ...}}
程序执行后的显示结果如下:01.59
3
299.3.2设计一个链表类模板
--参看书p226-228,9.3节定义一个处理链表的类模板list,它含有一个类型形参T,以指出每一链表项的data数据域的类型。
书p227中间处:T*get(){
改为:Tget(){
书p227倒L3行处:char*name;
改为:charname[20];
#include<iostream.h>#include<process.h>30
template<classT>classlist{
structnode{ Tdata; node*next;}*head,*tail;
/*
数据成员head与tail均为指针。其中的head总指向链表的首项,而tail总指向链表的尾项。每当准备往链表中加入一个表项(及其表项data数据)时,程序中首先使用“newnode”来动态生成一个新的表项空间,并“填入”该表项的data数据,而后通过指针的改变与关联,将该表项加入到以head为首以tail为尾的当前链表结构中(以形成一个更新后的链表)。 */31public:
list(){//构造函数,创建一个“空链表”
head=tail=NULL;};
voidInsert(T*item){ //动态生成链表项空间,并将item所指向的T型数据放至该项的data; //而后将新生成的该链表项插入到原链的链首(链表的“栈”式用法)。 ...};voidAppend(T*item){ //动态生成链表项空间,并将item所指向的T型数据放入该项的data; //而后将新生成的该项附加到原链的链尾(链表的“队列”式用法)。 ...};
Tget(){ //取出链表首项的数据(data域值),并将该首项从链表中删去 ...};};//类模板list定义结束
32
classperson{public:charname[20];
intage;floathight;};/*注:若说明为“char*name;”,则主函数处要通过new为每一对象的name域申请动态空间!*/
voidmain(){personps;list<int>link1;list<person>link2;
cout<<"---Input5person'sinformation---"<<endl;for(inti=0;i<5;i++){ //输入5个人的有关信息并进行处理 ...}
33
/*
上述循环结束后,link1和link2链表的逻辑结构及内容概示如下:
link1链表: head->0->1->2->3->4link2链表: head->4号人员信息->3号人员信息->2号人员信息->1号人员信息->0号人员信息*/
cout<<"-----Theresult----"<<endl;for(i=0;i<5;i++){
ps=link2.get();//取出link2链表首项的人员信息
link2.Append(&ps); //将刚从link2首取来的人员信息, //再一次附加到link2链表的链尾
cout<<<<""<<link1.get()<<endl; //输出link2链表人员信息的name, //以及link1链表表项中的对象的顺序号}}34程序执行后的显示结果如下:
---Input5person'sinformation---input0inf(name,age,hight):zhangLi201.68input1inf(name,age,hight):wangyue211.72input2inf(name,age,hight):liming191.75input3inf(name,age,hight):zhaoyi191.78input4inf(name,age,hight):chenjin201.8-----Theresult----chenjin0zhaoyi1liming2wangyue3zhangLi4359.3.3stack类模板中可变类型T型数据的
显示问题(特例函数)--参看书p225-226部分当处理某一类模板中的可变类型T型数据时,如果处理算法并不能对所有的T类型取值做统一的处理,此时可补充“特例函数”(也称“特殊版本”)来做特殊处理。
例如,当考虑某个类模板中的可变类型T型数据的屏幕显示问题时,就要考虑如下的两种情况:当T类型数据为int、char、double等基本类型的可直接通过运算符“<<”来显示的数据时,可以设一个模板函数对它们进行统一的处理;而当T为某种像complex这样的自定义复数类型时(此时无法直接通过“<<”来显示复数数据),就要设计并使用“特例函数”来单独对它进行处理。36classcomplex{ //复数类型complex doublereal,image;public: ...};
template<classT>classstack{Tdata[20];
inttop;public:voidshowtop(void);//显示栈顶数据...};
37template<classT>voidstack<T>::showtop(void) //通用的showtop//(T类型数据,可直接通过“<<”来一次性输出的数据){
if(top==0)
cout<<"stackisempty!"<<endl; else
cout<<"Top_Member:"<<data[top-1]<<endl;}
voidstack<complex>::showtop(void)//专用于complex类型的showtop//显示栈顶的那一个complex型数据,它不可直接通过“<<”一次性输出!{
if(top==0)
cout<<"sta
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 金属结构合同范本
- 空调外包承揽合同范本
- 租赁和使用合同范本
- 农村门面承租合同范本
- 环境维护合同范本
- 日本技能实习合同范本
- 电气配件购买合同范本
- 剪辑制作费合同范本
- 技术扶贫产业合同范本
- 皮革布料采购合同范本
- DB53∕T 1269-2024 改性磷石膏用于矿山废弃地生态修复回填技术规范
- 财务报表分析-第五章 营运能力分析
- mm立式矫直机辊系设计
- (教学设计)专题4 第2单元 基础课时13 羧酸的性质及应用2023-2024学年新教材高中化学选择性必修3(苏教版2019)
- 《建筑玻璃膜应用技术规程 JGJT351-2015》
- 2024年黑龙江龙东地区初中毕业学业统一考试中考物理试卷(真题+答案解析)
- 人教版音乐三年级下册第五单元 打字机 教案
- 国际物流专员聘用协议
- 2024年广东省公需课《百县千镇万村高质量发展工程与城乡区域协调发展》考试答案
- 2024年江西省初中学业水平考试数学试题卷
- 《小虾》学习任务群教学课件
评论
0/150
提交评论