第四篇 泛型编程
第12章 模板
现在的C++编译器实现了一项新的特性:模板(Template)。简单地说,模板是一种通用的描述机制,当使用模板时允许使用通用类型来定义函数或类等。通用类型可被具体的类型,如int,double甚至是用户自定义的类型来代替。模板引入了一种全新的编程思维方式,称为“泛型编程”或“通用编程”。
本章主要涉及以下知识点。
❑模板的用处:介绍了为什么要定义模板及其使用它的好处。
❑函数模板:介绍函数模板的定义及其使用。
❑类模板:介绍类模板的定义及其使用。
❑模板的嵌套:介绍如何在模板中再嵌套模板。
❑模板的参数:介绍模板中使用参数的注意事项。
12.1 为什么要定义模板
形象地说,把函数比喻为一个游戏过程,函数的流程就相当于游戏规则,在以往的函数定义中,总是指明参数是int型还是double型等,这就像是为张三(好比int型)和李四(好比double型)的比赛制定规则。可如果王五(char*型)和赵六(bool型)要比赛,还得提供一套函数的定义,这相当于又要制定一次规则,显然这是很麻烦的。模板的引入解决了这个问题,不管是谁和谁比赛,都把他们定义成A与B比赛,制定好了A与B比赛的规则(定义了关于A和B的函数)后,比赛时只要把A替换成张三,把B替换成李四就可以了,大大简化了程序代码量,保持了结构的清晰,提高了程序设计的效率。该过程称为“类型参数化”。
12.1.1 类型参数化
在讲解类型参数化之前,先来看一个示例代码12.1,如下所示。
代码12.1 函数重载FuncOverload
<———————————文件名:example1201.cpp———————————————-> 01 #include<iostream> 02 using namespace std; 03 int add(const int x,const int y)//定义两个int类型相加的函数 04 { 05 return x+y; 06 } 07 double add(const double x,const double y)//重载两个double类型相加的函数 08 { 09 return x+y; 10 } 11 charadd(charpx,const char*py)//重载两个字符数组相加的函数 12 { 13 return strcat(px,py);//调用库函数strcat 14 } 15 int main() 16 { 17 cout<<add(1,2)<<endl;//调用add(const int,const int) 18 cout<<add(1. 0,2.0)<<endl;//调用add(const double,const double) 19 char x[20]="Hello";//创建字符数组,注意要留够大小 20 char y[]="C++";// 21 cout<<add(x,y)<<endl;//调用add(char,char) 22 return 0; 23 }
输出结果如下所示。
3 3 Hello C++
【代码解析】代码12.1分别重载了3个add函数,这时,编译器将根据传递的参数决定应调用的函数,如下所示。
const int x=add(3,2);//调用int add(const int,const int) const double y=add(3.0,2.0);//调用double add(const double,const double)
如果需要处理其他类型,程序中必须提供相应的函数重载,这看起来会很麻烦,有没有其他更高效的方法呢?也许有的读者会认为可以通过宏定义#define add(a,b)((a)+(b))来实现,但是指针(C风格字符串)不能直接相加,而且调用宏时,编译器并不对其中的参数进行类型检查,这给程序带来了很大的安全隐患。
C++引入了模板来解决这个问题,如代码12.2所示。
代码12.2 函数模板FuncTemplate
<———————————文件名:example1202.cpp———————————————-> 01 #include<iostream> 02 #include<string>//使用标准类库中提供的string类时必须 03 //包含此头文件 04 using namespace std; 05 template<class T> 06 T add(const T&t1,const T&t2) 07 { 08 return t1+t2; 09 } 10 int main() 11 { 12 cout<<add(1,2)<<endl;//调用add(const int,const int) 13 cout<<add(1. 0,2.0)<<endl;//调用add(const double,const double) 14 string x("Hello,"),y("world"); 15 cout<<add(x,y)<<endl;//调用add(string,string) 16 return 0; 17 }
输出结果如下所示。
3 3 Hello,world
【代码解析】代码12.2仅仅采用如下形式定义了一个函数。
template<class T> T add(const T&t1,const T&t2) { return t1+t2; }
与之前一直讨论的函数定义相比,最大的不同在于代码第6行使用的add函数并没有指定参数的类型,而是用T来代替,现在,C++编译器可以根据我们调用sum函数的参数类型“现场”生成一个适当的函数,然后对其进行调用,这便是类型参数化。
对代码12.2定义的add函数而言,两个参数的类型必须一致,否则,编译器会报错,如下所示。
float a; double b; add(a,b);//错误,参数类型不一致当然,这并不是说使用模板只能用一种参数类型,关键在于函数是如何定义的,将add函数修改为如下所示。 template<class T1> T1 add(const T1&t1,const T2&t2) { return t1+t2; }
上述函数定义允许两个参数的类型不同,而且,返回类型与第一个参数一致。
下面学习模板的定义和使用。