当前位置:文档之家› 模 板

模 板

第14章模板模板是C++语言相对较新的一个重要特性。

模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现,方便了更大规模的软件开发。

本章介绍了模板的概念、定义和使用模板的方法,通过这些介绍,使渎者有效地把握模板,以便能正确使用C++系统中日渐庞大的标准模板类库。

第一节模板的概念若一个程序的功能是对某种特定的数据类型进行处理,则将所处理的数据类型说明为参数,就可把这个程序改写为模板。

模板可以让程序对任何其它数据类型进行同样方式的处理。

C++程序由类和函数组成.模板也分为类模板(class template)和函数模板(function template)。

因此,可以使用一个带多种不同数据类型的函数和类,而不必显式记忆针对不同的数据类型的各种具体版本。

函数模板的—般定义形式是:template<类型形式参数表>返回类型FunctionName(形式参数表){//函数定义体}其中的类型形式参数表可以包含基本数据类型,也可以包含类类型。

如果是类类型,则须加前缀class。

这样的函数模板定义,不是一个实实在在的函数,编译系统不为其产生任何执行代码。

该定义只是对函数的描述,表示它每次能单独处理在类型形式参数表中说明的数据类型。

当编译系统发现有一个函数调用:FunctionName(实在参数表);将根据实在参数表中的类型,确认是否匹配函数模板中对应的形式参数表,然后生成一个重载函数。

该重载函数的定义体与函数模板的函数定义体相同,而形式参数表的类型则以实在参数表的实际类型为依据。

该重载函数称模板函数(template function)。

->函数模板与模板函数的区别函数模板是模板的定义,定义中用到通用类型参数。

模板函数是实实在在的函数定义,它由编译系统在碰见具体的函数调用时所生成,具有程序代码。

->类模板的一般说明形式是:template<类型形式参数表>class className{//类声明体};template<类型形式参数表>返回类型className<类型名表>::MemberFuncName1(形式参数表){//成员函数定义体}template<类型形式参数表>返回类型className《类型名表::MemberFuncName2(形式参数表){//成员函数定义体}...template<类型形式参数表>返回类型className<类型名表>::MemberFuncNameN(形式参数表){//成员函数定义体}其中的类型形式参数表与函数模板中的意义一样。

后面的成员函数定义中,className<类型名表>中的类型名表。

是类型形式参数的使用。

这样的一个说明(包括成员函数定义),不是一个实实在在的类,只是对类的描述,称为类模板(class template)。

建立类模板之后,可用下列方式创建类模板的实例:className<类型实在参数表>object;其中类型实在参数表应与该类模板中的类型形式参数表匹配。

class_name<类型实在参数表>是模板类(template class),object是该模板类的一个对象。

->类模板与模板类的区别。

类模板是模板的定义,不是一个实实在在的类,定义中用到通用类型参数。

模板类是实实在在的类定义,是类模板的实例化。

类定义中参数被实际类型所代替。

第二节为什么要用模板1.关于函数考察两个swap()函数,一个交换两个整型数,另一个交换两个浮点数。

两个swap()的主体行为是一样的,无非一个是处理int型,一个是处理float型。

两个函数分别如下:void swap(int&a,int&b){int temp=a;a=b;b=temp;}swap(float&a,float&b){float temp=a;a=b;b=temp;}交换任何一对类类型对象,可以定义如下:void swap(T&a,T&b){T temp=a;a=b;b=temp;}能不能对于任一类型T的两个对象x1、x2,函数调用swap(x1,x2)总能使编译系统理解其交换意义而予以实现呢?若不然,每交换一对新类型的对象,都要定义一个执行同样操作的重载函数。

有了函数模板之后,重载就不必要了。

2.关于类再来考察一个链表类。

对于Cat类的链表,我们有:class Cat{//...};class CatList{public:CatList();void Add(Cat&);void Remove(Cat&);Cat*Find(Cat&);~CatList();private://...};该链表类将Cat类对象作为链表结点,进行插入、删除和查找处理。

所以称之为CatList 类。

同样如果想处理其他的任何一种类类型的对象作为结点的链表,我们必须重新对这链表进行定义。

这种工作很冗烦,因为所定义的类的行为没有任何变化,只是处理的结点之类型有所不同。

如果让类模板来工作,就能最大限度地解决这些问题。

第三节函数模板对于具有各种参数类型,相同个数,相同顺序的同一函数(重载函数),如果用宏定义来写:#define max(a,b)((a)>(b)?(a):(b))则它不能检查其数据类型,损害了类型安全性。

这也是为什么要使用函数模板的一个原因。

另外,如果一个个地定义其函数重载,例如,求同一数据类型数值中的最大值:int max(int a,int b){return a>b?a:b;}float max(float a,float b){return a>b?a:b;}对于与整数相容的char类型数据值的调用,也不能得到满意的结果。

如调用:max('3','5');则编译系统会为其找到一个int型的匹配,调用"int max(int a,int b);”函数,但是它将返回53而不是'5'(其ASCII码是53)。

用函数模板可将许多重载函数简单地归为一个,如下例所示://**********************//**ch20_1.cpp**//**********************#include<iostream.h>template<class T>T max(T a,T b){return a>b?a:b;//T类的>操作须有定义}void main(){cout<<"Max(3,5)is"<<max(3,5)<<endl;cout<<"Max('3','5')is"<<max('3','5')<<endl;}运行结果为:Max(3,5)is5Max('3,'5')is5当编译发现用指定数据类型调用函数模板时,就创建一个模板函数。

上例中,当编译程序发现max(3,5)调用时,它就产生了一个如下的函数定义,生成其程序代码:int max(int a,int b){return a>b?a:b;}当发现一('3','5')调用时,它又产生另一个如下的函数定义,也生成其程序代码:char max(char a,char b){return a>b?a:b;}这样,实参是什么数据类型,返回值也是什么数据类型,不会出现前面的问题。

而且模板又避免了相同操作的重载函数定义。

第四节重载模板函数可以像重载普通函数那样重载模板函数。

//**********************//**ch20_2.cpp**//**********************#include<iostream.h>#include<string.h>template<class T>T max(T a,T b){return a>b?a:b;}char*max(char*a,char*b){return(strcmp(a,b)?a:b);}void main(){cout<<"Max(\"Hello\",\"Gold\")is"<<max("Hello","Gold")<<endl;}运行结果为:Max("Hello","Gold")is Hello函数char*max(char*,char*)中的名字max与函数模板的名字相同,但操作不同,函数体中的比较采用了字串比较函数,所以有必要用重载的方法把它们区分开,这种情况就是重载模板函数。

编译程序在处理这种情况时,首先匹配重载函数,然后再寻求模板的匹配。

编译程序看到max("Hello","Gold")调用时,先进行重载函数匹配,结果匹配了非模板函数char max(char,char),所以这里不会为它产生模板函数的代码。

第五节类模板的定义链表操作并不依赖于要处理的链表的数据类型(如在上面所说的Cat类)。

定义类模板时,就是利用了这种独立性。

链表操作时,只是把要处理的数据类型当作参数。

一个类模板用于构筑一个通用链表。

如,整数链表,结构链表,以至任何其它定义过的数据类型的链表。

上节描述的CatList类也可以通过通用链表来构筑。

用类模板来定义一个通用链表,此时该通用链表还不是一个类定义,只是类定义的一个框架,即类模板。

下例定义了一个单向链表的模板类,它分别实现增加、删除、寻找和打印操作。

见图14-1:图14-1单向通用链表操作示意图增加时,Add()成员函数在链首挂接上一个携有T类型对象的结点,使之成为链首结点,由下列步骤①,②,③,④完成:①从堆空间申请一个结点;②将T类对象挂接在这个结点上;③将该结点指向链首的结点;④将该结点成为链首(链首指针pFirst指向它)。

删除时,Remove()成员函数寻找到挂接该对象的结点,先脱链,再删除结点。

由下列步骤⑤,⑥,⑦完成:⑤链中待删结点前后的两个结点链接起来,以使待删结点脱链;⑥删除待删结点上的T类对象(将空间还给堆);⑦删除待删结点(将空间还给堆)。

如果找不到对应结点,则无功而返;如果找到的是链首结点,则步骤⑤的脱链,由链首指针pFirst指向下一个结点来完成;删除结点须先将挂接的对象删除。

相关主题