Visual C++基础教程课件:模板_第1页
Visual C++基础教程课件:模板_第2页
Visual C++基础教程课件:模板_第3页
Visual C++基础教程课件:模板_第4页
Visual C++基础教程课件:模板_第5页
已阅读5页,还剩36页未读 继续免费阅读

下载本文档

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

文档简介

模板6.1函数模板

6.2类模板

习题

6.1函数模板在程序设计中经常会出现这样的情况:多个函数的参数个数相同,函数的代码(功能)也相同,但是它所处理的数据的类型不相同。对于这种情况,我们可以使用函数的重载定义多个函数名相同的函数。但即使是设计为重载函数也只是使用相同的函数名,函数体仍然需要分别定义。例如,第2章例2.17中对求绝对值函数的重载,三个函数只是参数的类型和返回值类型不同,其功能完全一样。实际上,若“提取”出一个可变化的类型参数T,则三个求绝对值函数可以“综合”成为如下的同一个函数:

Tabs(Tx) { returnx>=0?x:-x; }这正是函数模板的概念。使用函数模板可以避免函数体的重复定义,通过创建一个通用的函数,来支持多种不同类型的参数。实际上,标准C++库就建立在模板的基础上。函数模板的定义形式为:

template<class类型参数>

返回类型函数名(参数表) { 函数体 }其中,template是声明模板的关键字;“类型参数”是一个用户定义的标识符,用来表示任意一个数据类型。当在程序中使用函数模板时,系统将用实际的数据类型来代替“类型参数”,生成一个具体的函数,这个过程称为模板的实例化,这个由函数模板生成的具体函数称为模板函数。关键字“class”用来指定类型参数,与类无关,也可以使用typename来指定。【例6.1】定义求绝对值的函数模板,用来求不同数据类型值的绝对值。

#include<iostream.h>

template<classT>

Tabs(Tx)

{ returnx>=0?x:-x;

}

voidmain()

{ inti=50; doubled=-3.25; longl=-723358; cout<<abs(i)<<endl; //类型参数T被替换为int cout<<abs(d)<<endl; //类型参数T被替换为double cout<<abs(l)<<endl; //类型参数T被替换为long

}程序运行结果为:

50

3.25

723358

函数模板可以像一般函数那样直接使用。函数模板在使用时,编译器根据函数的参数类型来实例化类型参数,生成具体的模板函数。因此,程序中生成了三个模板函数:intabs(int),doubleabs(double)和longabs(long)。说明:

(1)在定义函数模板时,template所在的行与函数定义语句之间不允许有别的语句。例如,下面的程序段在编译时会产生错误:

template<classT> inti;//错误,中间不允许有别的语句

Tabs(Tx) { returnx>=0?x:-x; }

(2)在函数模板中允许使用多个类型参数。当函数模板中有多个类型参数时,多个类型参数间用逗号分隔,且每个类型参数前都必须有关键字class。【例6.2】具有两个类型参数的函数模板。

#include<iostream.h>

template<classT1,classT2>

voidmyfunc(T1x,T2y)

{ cout<<"x="<<x<<",y="<<y<<endl;

}

voidmain()

{ myfunc(15,"guo"); myfunc(5.3,42L); myfunc("li","yang");

}程序运行结果为:

x=15,y=guo

x=5.3,y=42

x=li,y=yang

此程序中,根据函数模板的调用生成了三个模板函数:voidmyfunc(int,char*)、voidmyfunc(double,long)和voidmyfunc(char*,char*)。

(3)函数模板的参数除了包含类型参数外,还可以包含具体的已存在的数据类型的参数。【例6.3】求数组元数的和的函数模板。

#include<iostream.h>

template<classT>

Tsum(T*array,intsize=0)

{ Ttotal=0; for(inti=0;i<size;i++) total+=array[i]; returntotal;

}

voidmain()

{ intiarray[]={1,2,3,4,5,6,7,8,9,10}; doubledarray[]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10}; cout<<sum(iarray,10)<<endl; //生成模板函数intsum(int*,int) cout<<sum(darray,10)<<endl; //生成模板函数doublesum(double*,int)

}

(4)函数模板可以重载,但要求参数的个数不同。【例6.4】函数模板的重载。定义两个同名的函数模板,分别求一个数组前size个元素的和与两个数组的前size个元素的和。

#include<iostream.h>

template<classType> //求数组前size个元素的和

Typesum(Type*a,intsize)

{ Typetotal=0; for(inti=0;i<size;i++) total+=a[i]; returntotal;

}

template<classType> //求两个数组前size个元素的和

Typesum(Type*a,Type*b,intsize)

{ Typetotal=0; for(inti=0;i<size;i++) total+=a[i]+b[i]; returntotal;

}

voidmain()

{ inta1[10]={1,2,3,4,5,6,7,8,9,10}; inta2[8]={2,4,6,8,10,12,14,16}; doubled[]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10}; cout<<sum(a1,10)<<endl;

//生成模板函数intsum(int*,int) cout<<sum(d,10)<<endl; //生成模板函数doublesum(double*,int) cout<<sum(a1,a2,8)<<endl; //生成模板函数intsum(int*,int*,int)

}

(5)由函数模板生成的模板函数没有数据类型转换的功能,实例化类型参数的各模板实参之间必须保持完全一致的类型,否则会产生错误。【例6.5】函数模板的错误调用示例。

#include<iostream.h>

template<classT>

voidswap(T&a,T&b)

{ Ttemp; temp=a; a=b; b=temp;

}

voidmain()

{ inti=10,j=20; doublex=4.5,y=8.3; chara='x',b='y'; cout<<"originali,j:"<<i<<''<<j<<endl; swap(i,j); //正确,生成模板函数voidswap(int,int) cout<<"swappedi,j:"<<i<<''<<j<<endl; cout<<"originalx,y:"<<x<<''<<y<<endl; swap(x,y);//正确,生成模板函数voidswap(double,double) cout<<"swappedx,y:"<<x<<''<<y<<endl; cout<<"originala,b:"<<a<<''<<b<<endl; swap(a,b); //正确,生成模板函数voidswap(char,char) cout<<"swappeda,b:"<<a<<''<<b<<endl; cout<<"originali,a:"<<i<<''<<a<<endl; swap(i,a); //错误

cout<<"swappedi,a:"<<i<<''<<a<<endl;

}程序中对函数模板的调用swap(i,a)产生了错误,因为编译器不能确定函数模板中类型参数T是int还是char,这里没有类型的自动转换,因而产生了二义性。为了解决这种二义性错误,可以采用两种方法:一是采用强制类型转换(见例6.6),二是采用非模板函数重载函数模板(见例6.7)。【例6.6】

#include<iostream.h>

template<classT>

Tmax(Tx,Ty)

{ returnx>y?x:y; }

voidmain()

{ inti=8; charc='a'; cout<<max(i,int(c))<<endl; //正确,生成模板函数intmax(int,int) cout<<max(char(i),c)<<endl; //正确,生成模板函数charmax(char,char)

}【例6.7】用普通函数重载函数模板解决例6.5中的错误。

#include<iostream.h>

template<classT>

voidswap(T&a,T&b)

{ Ttemp; temp=a; a=b; b=temp;

}

voidswap(int&a,char&b)

{ inttemp; temp=a; a=b; b=temp; cout<<"overloadedswap"<<endl;

}

voidmain()

{ inti=70,j=80; doublex=4.5,y=8.3; chara='x',b='y'; cout<<"originali,j:"<<i<<''<<j<<endl; swap(i,j); //生成模板函数voidswap(int,int) cout<<"swappedi,j:"<<i<<''<<j<<endl; cout<<"originalx,y:"<<x<<''<<y<<endl; swap(x,y); //生成模板函数voidswap(double,double) cout<<"swappedx,y:"<<x<<''<<y<<endl; cout<<"originala,b:"<<a<<''<<b<<endl; swap(a,b); //生成模板函数voidswap(char,char) cout<<"swappeda,b:"<<a<<''<<b<<endl; cout<<"originali,a:"<<i<<''<<a<<endl; swap(i,a); //正确,调用普通函数voidswap(int,char) cout<<"swappedi,a:"<<i<<''<<a<<endl;}程序运行结果如下:

originali,j:7080

swappedi,j:8070

originalx,y:4.58.3

swappedx,y:8.34.5

originala,b:xy

swappeda,b:yx

originali,a:80y

overloadedswap

wappedi,a:121P用普通函数重载函数模板时,普通函数的参数可以与函数模板生成的模板函数不同。例如本例中,函数模板不可能生成模板函数voidswap(int,char);普通函数的参数也可以与生成的模板函数的参数相同,但此时,重载的普通函数失去了类型转换的功能。例如在例6.6中可以用函数intmax(int,int)对函数模板进行重载。由于函数模板Tmax(T,T)也可以生成模板函数intmax(int,int),因此,重载的普通函数不能进行参数类型的转换。在C++中,函数模板的调用与同名函数的调用方式相同,系统按如下顺序进行处理:

(1)搜索程序中参数表与函数调用完全相同的函数,如果找到就调用它。

(2)检查函数模板经实例化后是否有相匹配的模板函数,若有就调用它。

(3)检查是否有函数可经参数的自动类型转换后实现参数匹配,若有则调用它。如果在以上三种情况下都没有找到匹配的函数,则按出错处理。6.2类模板函数模板只能定义非成员函数。使用类模板可以使得类中的某些数据成员和成员函数的参数及返回值取任意数据类型。例如,我们设计一个堆栈类,用来对某种数据类型的数据进行堆栈的操作。无论堆栈操作的数据是什么类型的值,它的操作方式都是相同的,只不过所操作的数据的类型不同。这时,就可以将要操作数据的类型进行参数化,定义一个类模板。类模板定义的格式如下:

template<class类型参数> class类名

{ //类的说明部分

};

例如,下面是一个简单类模板的示例。

template<classT>

classMyClass

{ private: Tx; public: voidSetX(Ta) { x=a; } TGetX() { returnx; }

};上面定义的类模板中,所有成员函数的定义都在类模板的说明部分。如果在类的实现部分定义成员函数,则应使用以下形式:

template<class类型参数>

函数返回类型类名<类型参数>::函数名(参数表) { 函数体 }其中,函数的返回类型、参数表中参数的类型和函数体类变量的类型都可以是template后说明的“类型参数”。类模板不代表一个具体的类,而是代表一组类。当使用类模板定义对象时,系统用指定的数据类型代替类型参数,从而将类模板实例化为某个具体的类,这称为模板类。使用类模板定义对象的格式如下: 类名<类型实参>对象名;其中,“类型实参”是任何已存在的数据类型,如果类模板带有多个类型参数,则必须为每一个类型参数指定相应的类型实参。【例6.8】用于对堆栈进行操作的类模板。

#include<iostream.h>

template<classType>

classStack

{ private: inttop,length; Type*s; public: Stack(int); ~Stack() { delete[]s; } voidPush(Type); TypePop();

};

template<classType>

Stack<Type>::Stack(intn)

{ s=newType[n]; length=n; top=0;

}

template<classType>

voidStack<Type>::Push(Typea)

{ if(top==length) { cout<<"Stackisfull!"<<endl; return; } s[top]=a; top++;

}

template<classType>

TypeStack<Type>::Pop()

{ if(top==0) { cout<<"stackisempty!"<<endl; return0; } top--; returns[top];

}

voidmain()

{ Stack<int>s1(10); Stack<double>s2(10); Stack<char>s3(10); s1.Push(11); s2.Push(1.1); s1.Push(22); s2.Push(2.2); s1.Push(33); s2.Push(3.3); cout<<"pops1:"; for(inti=0;i<3;i++) cout<<s1.Pop()<<''; cout<<endl; cout<<"pops2:"; for(i=0;i<3;i++) cout<<s2.Pop()<<''; cout<<endl; for(i=0;i<10;i++) s3.Push('A'+i); cout<<"pops3:"; for(i=0;i<10;i++) cout<<s3.Pop(); cout<<endl;

}程序运行结果如下:

pops1:332211

pops2:3.32.21.1

pops3:JIHGFEDCBA

程序中定义了一个类模板Stack。其中,数据成员length用来记录堆栈中数据的个数,top用来指示栈顶位置,数组s用来存放堆栈中的数据。使用Push()函数将数据压入堆栈,使用Pop()函数弹出栈顶的数据。在主函数中利用类模板定义了三个对象,分别用来对int、double和char类型的数据进行操作。类模板可以有多个模板参数,在关键字template后,多个类型参数间用逗号分隔,且每个类型参数前都必须有关键字class。例如,下面的示例创建使用两个类型参数的类模板。【例6.9】使用两个类型参数的类模板示例。

#include<iostream.h>

template<classT1,classT2>

classMyClass

{ private: T1x; T2y; public: MyClass(T1a,T2b) { x=a;y=b; } voidShow() { cout<<"x="<<x<<",y="<<y<<endl; }

};

voidma

温馨提示

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

评论

0/150

提交评论