所谓模板是一种使用无类型参数来产生一系列函数或类的机制,是C++的一个重要特性。模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现方便了更大规模的软件开发。
模板是以一种完全通用的方法来设计函数或类而不必预先说明将被使用的每个对象的类型。通过模板可以产生类或函数的集合,使它们操作不同的数据类型,从而避免需要为每一种数据类型产生一个单独的类或函数。
例如,设计一个求两参数最大值的函数,不使用模板时,需要定义四个函数:
int max(int a,int b){return(a>b)?a:b;}
long max(long a,long b){return(a>b)?a:b;}
double max(double a,double b){return(a>b)?a:b;}
char max(char a,char b){return(a>b)?a:b;}
若使用模板,则只定义一个函数:
Template<class type>type max(type a,type b)
{return(a>b)?a:b;}
在C++中,有两种模板,一种是函数模板,一种是类模板。
一. 函数模板:
1. 函数模板的一般说明形式如下:
template <模板形参表>
<返回值类型> <函数名>(模板函数形参表)
{
//函数定义体
}
其中, template 是一个声明模板的关键字。<模板形参表>可以包含基本数据类型,也可以包含类类型。类型形参需要加前缀class。如果类型形参多于一个,则每个类型形参都要使用class。<模板函数形参表>中的参数必须是惟一的,而且在<函数定义体>中至少出现一次。
函数模板只是说明,不能直接执行,需要实例化为模板函数后才能执行。
当编译系统发现有一个函数调用:
<函数名>(<实参表>);
时,将根据<实参表>中的类型生成一个重载函数即模板函数。该模板函数的定义体与函数模板的函数定义体相同,而<形参表>的类型则以<实参表>的实际类型为依据。
2.函数模板注意事项:
(1)函数模板也可以定义static,extern,inline函数,如,
templete <class T> inline T max(T a, T b) {...}
templete <class T> static T sum(T *array, int size = 0) {...}
(2)函数模板也可以像一般函数一样进行重载。一般来说,函数模板和重载函数各自的使用场景不同,比较如下:
重载函数适用于:函数名相同,算法亦相同,但参数个数或类型不同;
函数模板适用于:函数名相同,算法亦相同,参数个数亦相同,但类型不同。
(3) 函数模板举例:
/* 例1:templeteTest1.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 函数模板举例 */
/* 说明: 对具有n个元素的数组a[ ]求最小值,求最小值的函数设计成函数模板。*/
#include <stdio.h>
template <class T>
T min(T a[],int n)
{
int i;
T minv=a[0];
for(i=1;i<n;i++)
if(minv>a[i])
minv=a[i];
return minv;
}
int main()
{
int a[] = {1, 3, 0, 2, 7, 6, 4, 5, 2 };
double b[] = {1.2, -3.4, 6.8, 9.8 };
printf("min of a:%d /n", min(a,9));
printf("min of b:%f /n", min(b,4));
while(1);
return 0;
}
运行结果:
/* result:
min of a:0
min of b:-3.400000*/
例2:对于给定类型的两个变量,输出较大的一个
/* 例2:templeteTest2.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 重载模板函数举例 */
/* 说明: 对于给定类型的两个变量,输出较大的一个。*/
#include<stdio.h>
#include<string.h>
template <class type>type max(type a,type b)
{
return(a>b)?a:b;
};
char* max(char* a,char* b)
{
return(strcmp(a,b)>0?a:b);
};
static char *name[] = {"zhang", "wang","li","zhao"};
int main()
{
printf("100 and 9, max value is :%d /n", max(100,9)); // 100
printf("3.14 and 0.618, max is : %f /n/n", max(3.14, 0.618)); // 3.140000
for (int i = 0; i<4; i++)
{
printf("address of name[%d] is %p /n", i,&name[i]);
}
printf("/n");
printf("the max of %s and %s is: %s /n", name[0], name[1], max(name[0], name[1]) ); // zhang
printf("the max of %s and %s is: %s /n", name[2], name[3], max(name[2], name[3]) ); // zhao
while(1);
return 0;
}
运行结果:
/* result:
100 and 9, max value is :100
3.14 and 0.618, max is : 3.140000
address of name[0] is 00402000
address of name[1] is 00402004
address of name[2] is 00402008
address of name[3] is 0040200C
the max of zhang and wang is: zhang
the max of li and zhao is: zhao*/
在上面的例子中,对于模板函数max来说,当参数为字符串时,则类型参数type被编译器解释成 char *, 模板函数max则被解释成:
char *max(char *a, char *b)
{
return (a>b)?a:b;
}
这里,实际进行比较的是a,b的地址,由于b 定义在a之后,所以b比a 的地址高。
为了解决这种"没有真正比较字符串"的问题,定义了一个非模板函数max,通过调用strcmp去真正地进行字符串的比较。
编译器会优先选择一般函数,即非模板函数。
二. 类模板:
对象是类的特例,类又可以看作是类模板的特例。类模板又称类属类或类发生器,它们与函数模板有同样的应用目的。
1. 类模板说明:
类模板说明的一般形式是:
// 接口定义
template <类型形参表>
class <类名>
{
//类说明体
};
// 接口实现
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数1>(形参表)
{
//成员函数定义体
}
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数2>(形参表)
{
//成员函数定义体
}
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数n>(形参表)
{
//成员函数定义体
}
2. 使用类模板:
(1)使用:与函数模板一样,类模板不能直接使用,必须先实例化为相应的模板类,定义该模板类的对象后才能使用。
建立类模板之后,可用下列方式创建类模板的实例:
<类名> <类型实参表> <对象>;
其中,<类型实参表>应与该类模板中的<类型形参表>匹配。<类型实参表>是模板类(template class),<对象>是定义该模板类的一个对象。
(2)类模板与函数模板的不同:
a. 对由函数模板生成的模板函数的调用是由编译器自动决定的,
而对类模板的解释由程序设计者自行指明。
b. 格式定义上也有许多不同。
(3)类模板举例:
/* 例3:class_templeteTest1.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 说明: 对两个任意类型的数求和。*/
#include <stdio.h>
template<class T>
class A
{
public:
A();
A(T _a,T _b);
T sum();
private:
T a;
T b;
};
template <class T>
A<T>::A()
{
a=0;b=0;
}
template<class T>
A<T>::A(T _a,T _b)
{
a=_a;b=_b;
}
template<class T>
T A<T>::sum()
{
return (a+b);
}
int main()
{
A<int> ai(3,4);
A<double> ad(3.1,4.0);
printf("ai.sum() = %d /n", ai.sum());
printf("ad.sum() = %f /n", ad.sum());
while(1);
return 0;
}
运行结果:
/* result:
ai.sum() = 7
ad.sum() = 7.100000
*/
例4:一个类属向量类的实现
/* 例4:class_templeteTest2.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 说明: 一个类属向量类的实现*/
#include <stdio.h>
const int ARRAY_SIZE = 20;
// 接口定义
template<class T>
class Array
{
public:
Array( const int size);
~Array();
T &operator[](const int index);
void operator =(T temp);
private:
T *element;
int size;
};
//接口实现
template<class T> inline
Array<T>::Array( const int s )
{
size = s;
element = new T[size];
for (int i = 0; i < size; i++ )
element[i] = 0;
}
template<class T> inline
Array<T>::~Array()
{
delete element;
}
template<class T>
T &Array<T>::operator[](const int index )
{
return element[index];
}
template<class T>
void Array<T>::operator=(T temp )
{
for (int i = 0; i < size; i++ )
element[i] = temp;
}
int main()
{
Array<int> Iobj(ARRAY_SIZE);
Array<double> Dobj(ARRAY_SIZE);
Array<char> Cobj(ARRAY_SIZE);
for (int i = 0; i < ARRAY_SIZE; i++ )
{
Iobj[i] = i;
Dobj[i] = i;
}
Cobj = 'C';
for (int i = 0; i < ARRAY_SIZE; i++ )
{
printf("Iobj[%d] = %4d ", i, Iobj[i]);
printf("Dobj[%d] = %12f ", i, Dobj[i]);
printf("Cobj[%d] = %c ", i, Cobj[i]);
printf("/n");
}
while(1);
return 0;
}
运行结果:
/*
test result:
Iobj[0] = 0 Dobj[0] = 0.000000 Cobj[0] = C
Iobj[1] = 1 Dobj[1] = 1.000000 Cobj[1] = C
Iobj[2] = 2 Dobj[2] = 2.000000 Cobj[2] = C
Iobj[3] = 3 Dobj[3] = 3.000000 Cobj[3] = C
Iobj[4] = 4 Dobj[4] = 4.000000 Cobj[4] = C
Iobj[5] = 5 Dobj[5] = 5.000000 Cobj[5] = C
Iobj[6] = 6 Dobj[6] = 6.000000 Cobj[6] = C
Iobj[7] = 7 Dobj[7] = 7.000000 Cobj[7] = C
Iobj[8] = 8 Dobj[8] = 8.000000 Cobj[8] = C
Iobj[9] = 9 Dobj[9] = 9.000000 Cobj[9] = C
Iobj[10] = 10 Dobj[10] = 10.000000 Cobj[10] = C
Iobj[11] = 11 Dobj[11] = 11.000000 Cobj[11] = C
Iobj[12] = 12 Dobj[12] = 12.000000 Cobj[12] = C
Iobj[13] = 13 Dobj[13] = 13.000000 Cobj[13] = C
Iobj[14] = 14 Dobj[14] = 14.000000 Cobj[14] = C
Iobj[15] = 15 Dobj[15] = 15.000000 Cobj[15] = C
Iobj[16] = 16 Dobj[16] = 16.000000 Cobj[16] = C
Iobj[17] = 17 Dobj[17] = 17.000000 Cobj[17] = C
Iobj[18] = 18 Dobj[18] = 18.000000 Cobj[18] = C
Iobj[19] = 19 Dobj[19] = 19.000000 Cobj[19] = C
*/