1.9 模板
C++具有丰富的数据类型和严格的类型检查机制,这种机制极大限度地提高了编译器错误定位的准确性,也提高了编程的工作效率。但是细致的数据分类和严格的类型检查同时也带来了另外的麻烦:程序设计者总是在编制一些结构完全相同的函数和类,这些函数和类除去所用数据的类型不同外,对数据的处理方式和过程都一模一样。例如对于求两个参数中最大值的函数max(),由于类型检查的限制,必须为每种可能的数据类型组合定义一个函数。虽然C++中提供了函数重载机制,但那只能解决调用函数形式的一致性问题,函数定义中的重复工作还是令人无法忍受。以往人们总是利用宏来解决这个问题,但是宏所带来的不安全性也让人胆战心惊。为解决这个矛盾,C++提供了一种全新的语法现象——模板。
C++的模板机制允许将类型作为定义类或函数的参数。使用不同的参数,利用模板,编程者可以让编译器创建出不同的类或函数,这些不同的函数和类之间的差别仅在于函数或类中所用到的一些数据对象的类型不同。
1.9.1 函数模板
函数模板是一种抽象函数定义,它代表一类同构函数。通过用户提供的具体参数,C++编译器在编译时能够将函数模板实例化,即根据同一个模板创建出不同的具体函数。这些函数之间的不同之处主要在于函数内部一些数据类型的不同,而由模板创建的函数的使用方法与一般函数的使用方法则是相同的。函数模板的定义格式如下:
template<TYPE_LIST, ARG_LIST>Function_Definition
其中,Function_Definition为函数定义。TYPE_LIST被称为类型参数表,是由一系列代表类型的标识符组成的,其间用逗号分隔,这些标识符的通常是由大写字母组成的。ARG_LIST称为变量表,其中含有由逗号分隔开的多个变量说明,相当于一般函数定义中的形式参数。
注意 变量表和类型表不必同时出现,当没有变量表时应去除两表之间的逗号。上述格式中的尖括号<>是格式的一部分。
定义求两个数据中最大值的函数模板。
#include "iostream.h" template <class TYPE> TYPE GetMax(TYPE x, TYPE y) { return x>y? x:y; } void main() { int n1(4), n2(8); double d1(2.1), d2(2,3); cout<<GetMax(n1, n2)<<endl; cout<<GetMax(d1, d2)<<endl; cout<<GetMax<int>(d1, d2)<<endl; }
主程序中的第三行相当于调用int GetMax(int x, int y),第四行相当于调用函数double GetMax(double x, double y),而第五行则利用double型的参数强制调用int GetMax(int x, int y)形式的函数。
1.9.2 特定模板函数
定义了函数模板后,使用函数模板可以创建不同的函数实例,但是对一些特殊类型,不适合于应用函数模板创建实例。例如以类型char *调用例12.12中的函数模板,则上述模板的实例不是比较字符串大小,而是比较两个字符串指针的大小,对于这样的特殊情况通常需要定义一个特定参数类型的模板函数来解决,这个函数称为特定模板函数。例如:
char * max(char * c1, char * c2) { return(strcom(c1, c2)>0)? c1:c2; }
由于C++进行参数匹配的顺序是首先寻找参数类型完全匹配的函数,因此对于如下调用方式,C++将直接调用上述特定模板函数。
char * s1="abc"; char * s2="defk"; cout<<max(s1, s2)<<endl;
1.9.3 类模板
(1)定义
C++提供的类模板是一种更高层次上的抽象的类定义,用于使用相同代码创建不同的类。类模板的定义与函数模板的定义类似,只需把函数摸板中的函数定义部分换为类说明,并对类的成员函数进行定义即可。在类说明中可以使用出现在TYPE_LIST中的各个类型标识,以及出现在ARG_LIST中的各变量。例如,下述程序定义了一个类模板:
template<class TYPE, int i> class MyTemplateClass { public: MyTemplateClass(void) {} ~MyTemplateClass(void) {} int MemberSet(TYPE a, int b); private: TYPE Tarray[i]; int arraysize; };
这个程序段定义了类模板MyTemplateClass,类中有两个数据成员,其中的Tarray[i]是一个具有类型TYPE的i个元素的数组,类型TYPE具体是什么类型将由用户在创建模板类实例时给出,i的具体数值也将同时给出。上述类的定义尚不完整,因为成员函数MemberSet()的具体定义尚未给出。定义类模板中成员函数的方法可以参见下述代码:
template <class TYPE, int i> int MyTemplateClass<TYPE, i>::MemberSet(TYPE a, int b) { //对于第b个元素赋予具有类型TYPE的数据 if((b>=0)&&(b<i)) {//如果b在数组下标取值范围内 Tarray[b]=a; return sizeof(a); } else return -1; }
可以看出,类模板成员函数的定义同函数模板的定义相似,只是在具体成员函数名前加上了一个域指示,说明所定义的函数属于类模板MyTemplateClass。
(2)类模板对象
类模板被称为参数化类型。使用类模板时,首先要提供参数,C++编译器根据这些参数和类模板定义自动产生所有成员函数,从而将类模板实例化。例如:
MyTempClass<float,6>test1; MyTempClass<char, items++>test2; //错误!第二个模板参数必须为常数 MyTempClass<int, 9>test3;
上述第一条语句中的<float,6>告诉编译器用参数float和6替代类模板中的标识符TYPE和i创建一个类,然后创建该类的一个对象test1。