第13章 模板
第三篇 泛型程序设计与模板库
第13章 模板
(本资料qq讨论群112133686)模板是C++语言的重要特征,使用能够显著提高编程效率。利用C++的函数模板和类模板,能够快速建立具有类型安全的类库集合和函数集合,进行大规模软件开发,同时提高软件的通用性和灵活性。C++的标准模板库(standard template library,简称STL)编程完全依赖模板的实现。本章举的实例都是泛型编程的实例,如怎样定义模板函数、类模板、泛型编程等。C++的标准模板库是把一些常用的数据结构比如链表、数组、二叉树等和算法比如排序、查找等写成模板,以后则不论数据结构里放的是什么对象,算法针对什么样的对象,都不必重新实现数据结构,重新编写算法。
本章重点讲解了泛型编程举例及简单应用、类模板及容器实例、数据结构实例,这些对于初学者反复多实践,加深理解,举一反三。
13.1 为什么定义模板
所谓模板是一种使用无类型参数来产生一系列函数或类的机制。若一个程序的功能是对某种特定的数据类型进行处理,则可以将所处理的数据类型说明为参数,以便在其他数据类型的情况下使用,这就是为什么药定义模板的原因。
案例13-1 绕过形参限制
【案例描述】
本章开始演示泛型编程技术的实例,简单地说就是使用模板的程序设计实例。泛型程序设计(generic programming)的思想是基于模板机制以及标准模板库STL的实现技术。本实例从最简单模板说起,针对多个数据类型取最大数。本例效果如图13-1所示。
【实现过程】
声明函数模板T,定义函数体GetMax,功能求最大数,主函数中两次调用声明函数模板,输出结果。代码如下:
#include <iostream.h>
#include <iostream>
template <class T> //声明函数模板
T GetMax (T a, T b) { //定义函数体
T result;
result = (a>b)? a : b; //取最大数
return (result);
}
int main () {
int i=15, j=6, k;
double l=10.6, m=8.5, n;
k=GetMax(i,j); //调用函数模板
n=GetMax(l,m);
cout <<"输出最大整数:"<< k << endl; //打印结果
cout <<"输出最大浮点数:"<< n << endl;
system("pause");
return 0;
}
【案例分析】
(1)模板函数 GetMax(),类型T可以被用来声明新的对象,T result;result是一个T类型的对象, 就像a和b一样,也就是说,它们都是同一类型,这种类型就是当调用模板函数时写在尖括号<>中的类型。在这个具体的例子中,通用类型 T 被用作函数GetMax()的参数,不需要说明<int>或 < double>,编译器也可以自动检测到传入的数据类型代码。第一次调用函数模板实际上函数模板函数int abs(int x),第二次为double abs(double x)。
(2)模板是以一种完全通用的方法来设计函数或类而不必预先说明将被使用的每个对象的类型。通过模板可以产生类或函数的集合,使它们操作不同的数据类型,从而避免需要为每一种数据类型产生一个单独的类或函数重复代码。
提示:模板并非通常意义上可直接使用的函数或类,仅仅是对一族函数或类的描述,是参数化的函数和类。模板是一种使用无类型参数来产生一族函数或类的机制。
案例13-2 定义一个实现计算的静态模板
【案例描述】
使用模板的目的就是能够让程序员编写与类型无关的代码。本实例定义一个静态模板类实现相加、相乘、判断功能。本例效果如图13-2所示。
图13-2 定义一个实现计算的静态模板
【实现过程】
建立一个静态模板类T,类中有相加、相乘、判断功能,主函数调用这些功能,并且输出结果。代码实现如下:
#include <iostream>
using namespace std;
template <class T>
class Operate{ //建立一个静态模板类
public:
static T Add(T a,T b){return a+b;} //相加
static T Mul(T a,T b){return a*b;} //相乘
static T Judge(T a,T b=1){
if(a>=0){ //判断
return a;
}
else{ a/b;
} } };
int main(){
int A,B,C,D,E,x,y,z; //定义整型变量
A=10,B=12,C=13,D=40,E=50; //初始化
x=Operate<int>::Add(A,B); //调用函数
y=Operate<int>::Mul(C,D);
z=Operate<int>::Judge(E,B);
cout<<"Add="<<x<<'\n'<<"Mul="<<y<<'\n'<<"Judge="<<z<<endl;
system("pause");
return 0;
}
【案例分析】
模板是C++语言最强大又是最少使用的特征之一。在C++语言中,模板让程序员能够定义一种使用不同类型对象的行为。表面上看有点像宏,但是宏不是类型安全的,而模板是类型安全的。模板声明以关键字template和一个参数列表组成,声明格式如下:
template<parename list>。
13.2 函数模板
案例13-3 求N*N的值
【案例描述】
数学运算中虽然给出了标准的算法,但是输入的数据类型不确定,要编写几个函数实现算法。这个时候就可以用到函数模板,本实例举个矩阵相乘的函数模板。效果如图13-3所示。
图13-3 求N*N的值
【实现过程】
程序定义两个矩阵模板函数multi()和output(),分别实现矩阵的乘法和输出矩阵的数据,主函数定义3个整数数组result、matrix1和matrix2,存储输出结果、输入矩阵、实现矩阵相乘和输出结果。其代码如下:
#include <iostream>
#include <iomanip>
using namespace std;
//矩阵模板函数
template <typename T1,typename T2>void multi(T1 *mat1,T2 *mat2,T2 *result,int a,int b,int c);
template <typename T>void output(T *mat,char*s,int a,int b);
int main(){
int result[6][4];
int matrix1[6][3]={7,11,12,23,2,3,5,7,9,1,4,6,34,45,56,2,3,6};
int matrix2[3][4]={2,3,1,0,-1,-2,5,3,7,6,5,4};
char *s1="result"; //定义字符串
multi(matrix1,matrix2,result,6,3,4); //调用矩阵相乘
//显式:multi <int[3],int[4]> (middle,matrix2,result,6,3,4);
output(matrix1,"matrix1",3,6);
output(matrix2,"matrix2",3,4);
output(result,s1,6,4);
system("pause");
return 0;
}
template <typename T1,typename T2>void multi(T1 *mat1,T2 *mat2,T2 *result,int a,int b,int c){
//二维数组的类型仅指其组成元素类型(一维数组),而与元素数量无关
//如matrix2和result是同一类型,其元素同为整型4元素一维数组,尽管前者只有3个元素,后者有6个
int i,j,k;
for (i=0;i<a;i++){
for (j=0;j<c;j++){ //矩阵的列
result[i][j] = 0;
for (k=0;k<b;k++) //矩阵的行
result[i][j]+=mat1[i][k]*mat2[k][j];
}
}
return;
}
template <typename T>void output(T *mat,char *s,int a,int b){
int i,j;
cout<<s<<endl;
for (i=0;i<a;i++){ //矩阵的行
for (j=0;j<b;j++) //矩阵的列
cout<<setw(6)<<mat[i][j];
cout<<endl;
}
return;
}
【案例分析】
(1)代码mat1[i][k]*mat2[k][j],构成的矩阵称为矩阵A与B的乘积。
(2)multi(matrix1,matrix2,result,6,3,4);调用矩阵相乘,就是函数模板的显式实例化multi <int[3],int[4]> (middle,matrix2,result,6,3,4);。
案例13-4 万能计算器(支持各类数据的加法函数)
【案例描述】
假如设计一个求输入的两参数求加、减的函数,因涉及到几个数据类型可能需要定义几个函数,这几个函数几乎相同,唯一的区别就是形参类型不同,需要事先知道有哪些类型会使用这些函数,对于未知类型这些函数不起作用。本实例用一个模板就可以实现数学四则运行。效果如图13-4所示。
图13-4 万能计算器(支持各类数据的加法函数)
【实现过程】
声明函数模板T,定义函数体Add()、Substraction()和display(),功能求加法、减法和显示输入计算的数,主函数中两次调用声明函数模板,输出结果。代码如下:
#include <iostream>
using namespace std;
template <class T> //声明函数模板
T Add (T a, T b) { //定义函数体,加法
T result;
result = a+b;
return (result);
}
template <class T>
T Substraction (T a, T b) { //定义函数体,减法
T result;
result = a-b;
return (result);
}
template <class T>
void display (T a, T b) { //显示输入计算的数
T result;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
}
int main(int argc, char* argv[])
{
int a=5, b=6;
display(5,6); //显示输入计算的数
cout << "a+b Add():" << Add(a,b) << endl; //调用函数模板
cout << "a-b Sub():" << Substraction(a,b)<<endl<<endl;
double l=30.23, m=40;
display(l,m);
cout << "a+b Add():" << Add(l,m) << endl; //调用函数模板
cout << "a-b Sub():" << Substraction(l,m) << endl;
system("pause");
return 0;
}
【案例分析】
(1)函数模板声明了类型T,表示一种抽象的类型。当编译器检测到程序中调用函数模板Add,便用Add的第一个实参的类型替换掉整个模板定义中的T,模板函数与重载是密切相关的,从函数模板产生的相关函数都是同名的,编译器用重载的解决方法调用相应的函数。另外函数模板本身也可以用多种方式重载。
(2)函数模板的实例化是在编译中,按系统处理函数调用时由系统自动完成。在调用函数模板时,系统首先确定模板参数所对应的具体类型,并依据该类型生成一个具体函数,系统实际上是调用了这个具有确定参数类型的函数。
提示:模板函数是函数模板的一个具体实例,是模板参数实例化后的一个可执行的具体函数,只处理一种确定的数据类型。
案例13-5 用户与管理员(判断若是布尔类型则使用特化例程)
【案例描述】
C++语言引入特化来解决某些类型在函数中的特殊操作。当编译器寻找到函数调用特化后,使用特化的定义,不再使用模板函数。本例效果如图13-5所示。
图13-5 用户与管理员(判断若是布尔类型则使用特化例程)
【实现过程】
定义函数模板Greater,进行特化声明template<> bool Greater(bool,bool);,主函数调用两次Greater,实现隐式实例化和优先调用特化函数。代码如下:
#include <iostream>
using namespace std;
template<class T>
T Greater(T x,T y);
template<> bool Greater(bool,bool); //特化声明</