《C++程序设计语言》课件第12章_第1页
《C++程序设计语言》课件第12章_第2页
《C++程序设计语言》课件第12章_第3页
《C++程序设计语言》课件第12章_第4页
《C++程序设计语言》课件第12章_第5页
已阅读5页,还剩85页未读 继续免费阅读

下载本文档

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

文档简介

第12章模板

12.1概述12.2类模板12.3函数模板12.4模板与继承12.5综合示例小结练习题本章要点:

模板的基本概念;

类模板的定义方法;

函数模板的定义方法;

模板与继承。

12.1概述

第7章我们讨论了函数及其相关问题。函数是具有明确涵义的、能完成某种特定功能的一系列语句的抽象。它用函数体中的语句表示所抽象的语句序列中相同的部分,用形参指明语句序列中不同的部分,用实参体现其不同的部分。

函数具有参数化机制。函数的实参是某种类型的数据(即为变量、字面值或常量的值)、地址(指针和引用)或它们构成的表达式。那么,还有没有其它类型的参数也是我们所需要的?通过前面的学习我们已经知道:类型(基本类型和自定义类型)是一种实例化机制,即我们可用一个类型产生该类型的多个实例。当给自定义类型的构造函数以不同的实参时,可以用该类产生初始状态不同的多个对象。那么,还有没有其它方式的实例也是我们所需要的?

首先,让我们考察一下下面两个类的定义:

classStringVector{//String向量类

String*v;

intsz;从上述两个类的定义中我们可看出:这两个类的内涵十分相似,都是向量,差别仅在于它们是不同元素类型的向量;其所定义的数据结构和操作亦十分相似,差别仅是类型而不是数据或地址。

如果能够把类型作为参数,就可能用另一种抽象结构产生这两个类乃至更多的向量类。

允许将类型作为参数的抽象结构称为类属(Generic)。支持类属的程序设计范型称为类属程序设计(GenericProgramming)。

在C++中体现类属的机制是模板(Template)。C++的模板机制为类属程序设计提供了直接的支持,它允许程序员用类型作为参数来产生新的类或函数。在C++中模板分为类模板(ClassTemplate)和函数模板(FunctionTemplate)两类。一个类模板是一簇具有相同数据结构和操作的相似类(只是数据结构的元素类型或操作的类型不同)的抽象。当我们用类型作实参时,用类模板可实例化出一个特定的类,称为模板类(TemplateClass);一个函数模板是一簇具有相似功能的,其所操作的数据类型不同的函数抽象。当用类型作实参时,用函数模板可实例化出一特定的函数,称为模板函数(TemplateFunction)。

在上面两个相似的类中,采用C++的模板机制,我们可抽象出一向量类模板:

template<classT>classVector{//类模板Vector的定义

T*v;

intsz;

public:

T&operator[](inti);

intsize(){returnsz;}

Vector(intvectorSize);

/*...*/

};

上述模板类定义中的template<classT>指明这是一个类模板的定义,它有一个类型参数T(T可为基本类型或自定义类型名)。当程序员以类型String为实参时,可从该类模板中生成一具体的StringVector类;当程序员以类型Complex为实参时,可从该类模板中生成一具体的ComplexVector类。一个类是一组对象的抽象;一个类模板是一组类的抽象。

引入类模板的目的就是对结构特征和行为特征相似、但类的成员函数或操作的类型不能保证相同的一组类进行高一级的抽象,以提高程序的重用性和规格化程度。

类模板的一个重要作用是对类库提供了强有力的支持,因为类库中的类/函数需要更高的抽象,以达到其通用性。C++ 的标准库函数及STL库中的类都是采用模板机制实现的。

12.2类模板

12.2.1类模板的定义

类模板为一簇类提供了一个框架,类模板的定义声明:

template<classT>

classmyClass{//...};

在类模板的定义声明中,关键字template告诉编译器这里定义的是一个类模板,该类模板只有实例化后所形成的模板类才能在程序中使用。类型参数T的实参为一类型名(基本类型名或自定义类型名),模板参数的虚实结合发生在编译期,从类模板生成模板类及从模板类生成一具体的对象在编译期同时发生。下面我们以一堆栈类模板为例来讲述C++类模板的一般定义方法及其相关问题。

堆栈是独立于栈中元素类型的一种通用概念,但是,每一种堆栈元素的类型又都是具体的。因此,对于这个问题我们可以将这种通用概念上的堆栈用类模板来表示,用类型参数化机制来定制一个具体的堆栈,那么,一个具体的堆栈就是这个通用类模板的一个具体实例。下面给出一个堆栈类模板的定义。

12345678910

Stackisfull.Cannotpush11

PoppingelementsfromintStack

10987654321

Stackisempty.Cannotpop

从上述堆栈类模板的定义中我们看到,类模板的定义与通常的类的定义没有太大差别,只是:

(1)类模板的定义应以template<classT>开头,它表明这是一个类模板的定义,并且有一类型参数T,T为某一自定义类型名或基本类型名。

(2)堆栈类模板的压栈与弹栈操作是针对模板参数类型T的,而不是针对某一特定的、具体的数据类型。

(3)堆栈指针类型为T。

(4)堆栈类模板成员函数的实现若在模板类外定义,则亦应以template<classT>开头,并且类模板Stack后应跟<T>参数表。12.2.2类模板参数及其限制

在定义类模板时,一个类模板的参数表中可以声明多个形参。其形参既可取classT的形式(实参要求为一类型名),亦可取Tx的形式(实参要求为一T类型的值)。另外,在类模板参数表中先声明的类型参数可以立即用来声明同一参数表中的其它形参。例如:

template<classT,Tdef_val>//第一个类型参数可作为第二个参数的类型

classMyclass{//...};

C++语法规定,合法的模板实参为

类型名(形参须以classT关键字开头);

常量表达式(形参的类型为T,T为基本类型名,实参为各种基本类型的常量表达式,但字符串字面值除外);

具有外部链接性的对象名或函数的地址,形式如下:

&of; //of为对象或函数名

f; //f为一函数名

非过载的指向类成员的指针:

&X::of; //of是指向类X成员的指针

在模板的形参参数表中,还可有形如Tx的参数,其中T为基本类型名。若T为int,则该参数常用于提供类模板(常常为一容器类模板)的大小或者其界限。例如:

template<classT,inti> //i为整型模板参数,用于提供Buffer类模板的大小

classBuffer{

Tv[i];

intsz;

public:

Buffer():sz(i){}

//…

};

特别应注意的是,若模板的形参为Tx(T为基本类型),其实参只能是一个T类型的常量。下面代码片段中由于i是一个变量,因而在类模板实例生成时编译器报错。

voidf(inti) //i是一个变量

{

Buffer<int,i>bx;//在类模板的实例生成时,编译器报错

}

从一个类模板通过相应的模板实参生成一个模板类(即一个具体的类)的过程通常称为模板的实例化(TemplateInstantiation)。

C++对模板的形/实参有如前所述的若干限制。还应特别注意的是,在用类模板产生实例类时,与用这个实例类产生的对象须同时进行。这关键在于用类模板产生的类是在编译时而不是在运行时进行的。尽管类模板在表示和使用上很像一个类,但它们之间存在着本质上的区别(至少对于C++语言是这样),即类模板的实例不是对象,它是一个具体的(模板)类。一个类模板除了可以生成实例(类)以外,再没有任何操作可以作用于类模板的任何实例,其实例的状态(也就是一个类的基本结构)一经生成便不可改变。显然,无法用操作来改变状态的实体不是对象。

C++语法规定:类模板不允许过载。

12.3函数模板

12.3.1函数模板的定义

在C++中,另一类模板就是函数模板。一个函数模板是一簇相似函数的抽象,只是其操作对象具有不同的类型而已。函数模板对于编写通用型算法是至关重要的。定义函数模板时亦是以template<classT>开始的。请看下面的函数模板实例:

//排序函数模板

template<classT>

voidsort(Vector<T>&v)

//对Vector中的元素(类型为T)进行排序一旦定义了sort函数模板,则对于sort的每一个函数调用,编译器都能从函数调用所给的实参推断出传递给函数模板形参的类型(另外,还能够从该调用中推断出类型参数和非类型参数),继而生成函数模板实例(一具体的sort模板函数),同时进行该模板函数的函数调用,即函数模板的实例生成与函数调用是在编译期同时发生的。12.3.2函数模板的重载

C++规定:函数模板可以重载。它既可以用函数模板重载,也可以用普通函数重载。之所以允许重载是因为函数模板的参数T在实例化时实参类型无隐式转换功能。下面是函数模板重载的实例:

template<classT>//求两个同类型T的变量中的最大者

Tmax(Tx,Ty)

{return(x>y)?x:y;}

上例代码中的max(i,c)和max(f,i),由于模板函数在调用时其参数类型不同,所以编译器报错。

欲解决上述问题,可用一普通函数重载函数模板,此时,只需声明该普通函数的接口即可。请看下述示例代码:

template<classT> //函数模板的定义

Tmax(Tx,Ty)

{

return(x>y)?x:y;

}

doublemax(double,double);

//重载上述函数模板,重载时只需给

出函数接口该函数的作用是比较两个字符串指针,而不是比较两个指针所指向的字符串的内容,这与我们所定义的函数模板的语义相违背。因此,此时须提供一个可以替换该函数模板实例的函数,用来替换的函数称为特定的模板函数,即:

char*max(char*c1,char*c2)

{

return(strcmp(c1,c2))?(c1):(c2);

}12.3.3函数调用的匹配原则

在一个C++程序中,有可能既有普通函数,又有模板函数。因此,对于每一个函数调用,C++编译器在匹配函数时遵循以下约定:

寻找一个参数类型、个数完全匹配的函数,若找到,则调用它。

寻找一个函数模板,将它实例化成一个匹配的模板函数,若找到,则调用它。

试试能否找到一个函数,经过隐式的类型转换,对所给的参数进行匹配。若找到,则调用它。

若通过上述方法找不到一个合适的函数,则返回错误信息。

若第一步有多于一个的选择,函数调用将返回错误信息。12.3.4编写函数模板时的注意事项

利用函数模板生成模板函数时,由于模板的参数类型不同,各类型对同一操作将有不同的语义,这是常见的,特别是比较操作和赋值操作。例如:在上述函数模板定义中,不同的类型对运算符“<”和“=”有不同的语义。因此,在定义函数模板时,如果其中需要使用与类型参数相关的操作,则需要:

所有可能的实参类型都过载定义了这样的操作(但这通常是不可能的);

干脆在参数表中定义相应的函数指针形参(需要统一规定函数的接口),在类模板中调用其实参(与类型相关的操作的函数地址)以进行这样的操作。

下面我们给出一实例,来阐述这种用法。

/*

定义类模板SortedSet,即元素有序的集合,集合元素的类型和集合元素的最大个数可由使用者确定。要求该类模板对外提供三种操作。

(1) insert:加入一个新的元素到合适的位置上,并保证集合元素的值不重复。

(2) get:返回比给定值大的最小元素的地址。若不存在,返回。

(3) del:删除与给定值相等的那个元素,并保持剩余元素的有序性。

12.4模 板 与 继 承

模板和继承都是用来产生新的类型的机制。将这两种机制结合在一起,构成了许多有用的技术基础。这些技术经常在开发类库时使用。C++语法规定:

(1)一个类模板可以继承另一个类模板。例如:

template<classT>

classVec:publicVector<T>{

//...

};

(2)一个类模板也可以继承另一个类。例如:

template<classT>

classTeacher:publicPerson{

//...

};

(3)一个类模板甚至在可以继承另一个类模板时将自己的实例化类型作为父类模板的实参。例如:

template<classT>

classBasic_ops{

//...

};

template<classT>

classMath_container:publicBasic_ops<Math_container<T>>{

//...

};

(4)一个类模板中的数据成员的类型,可以用另一个类模板的实例来定义。例如

template<classT>

classTeacher:publicPerson{

Vector<T*>group;//类模板的数据成员为一类模板的实例

//...

};

Teacher<Student>t;上述技术方案中,最常用、最有用的就是从类模板继承类模板,该方法将额外的功能添加到现有的模板类中。下面我们通过示例来帮助理解上述知识。

例1

类模板继承另一个类。

constintMAXSIZE=100;

classBaseClass{//虚基类

protected:

virtualintgetNumberOfElements()=0;

virtualintisFull()=0;

virtualintisEmpty()=0;

};

12.5综合示例

例2

单向链表的设计与实现,对象的组织结构见图12.1。图12.1单向链表中对象的组织结构例3

定义类模板

温馨提示

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

评论

0/150

提交评论