在创建完成抽象操作的函数时,如:拷贝,反转和排序,你必须定义多个版本以便能处理每一种数据类型。以比较两个数的大小为例:
#include<iostream>
using namespace std;
int MAX(int a, int b)
{
return a > b ? a:b;
}
double MAX(double a, double b)
{
return a > b ? a : b;
}
int main()
{
cout<<"较大的是:"<<MAX(1, 2)<<endl;
cout << "较大的是:" << MAX(1.1, 2.1) << endl;
system("pause");
return 0;
}
通过这个函数可以发现:(1)只要输入的数据类型改变,就要添加相应的函数类型;
(2)除了类型不同以外,函数代码均相同,代码复用率低;
(3)一个出现问题,所有的函数都有问题,不好维护;
那么有没有更好的方法解决这个问题呢?当然有啦,运用泛型编程就可以很好地解决它。
泛型编程:编写与类型无关的逻辑代码,是代码复用的一种重要手段。
模板是实现它的重要基础。
模板函数:函数模板的数据类型参数标识符实际上是一个类型形参,在使用函数模板时,要将这个形参实例化为确定的数据类型。将类型形参实例化的参数称为模板实参,用模板实参实例化的函数称为模板函数。模板函数的生成就是将函数模板的类型形参实例化的过程。
定义格式: 函数模板允许使用多个类型参数,但在template定义部分的每个形参前必须有关键字typename或class,即:
template<class 数据类型参数标识符1,…,class 数据类型参数标识符n>
<返回类型><函数名>(参数表)
{
函数体
}
例如:
template<class T>
T MAX(T a, T b)
{
return a > b ? a : b;
}
int main()
{
cout<<"较大的是:"<<MAX(1, 2)<<endl;
cout << "较大的是:" << MAX(1.1, 2.1) << endl;
cout << "较大的是:" << MAX('A', 'B') << endl;
system("pause");
return 0;
}
那么模板函数的编译过程是怎样的呢?
在编译的时候:
1)实例化前,先检查模板代码本身,查看语法错误等,如遗漏分号等;
2)在实例化期间,检查模板代码,查看是不是所有的调用都有效。如该实例化类型不支持某些函数的调用。
模板形参数:分为模板类型参数、非类型参数
(1)类型参数:类型形参由关见字class或typename后接说明符构成,如template<class T> void h(T a){};其中T就是一个类型形参,类型形参的名字由用户自已确定。模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。
(2)非类型参数:模板的非类型形参也就是内置类型形参,如template<class T, int a> class B{};其中int a就是非类型的模板形参。
要注意的是:
a、非类型模板的形参只能是整型,指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
b、调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
template<class T>
const T& MAX(const T& a, const T& b)
{
return a>b ? a : b;
}
int main()
{
cout<<"较大的是:"<<MAX(1, 2)<<endl;
cout << "较大的是:" << MAX(1.1, 2.1) << endl;
cout << "较大的是:" << MAX('A', 'B') << endl;
system("pause");
return 0;
}
模板实例化:有显示和隐式两种
显示实例化:
template<class T>
const T& MAX(const T& a, const T& b)
{
return a>b ? a : b;
}
int main()
{
cout<<"较大的是:"<<MAX(1, 2)<<endl;
cout << "较大的是:" <<MAX<int>(1, 2.1) << endl;//显示实例化
cout << "较大的是:" << MAX('A', 'B') << endl;
system("pause");
return 0;
}
MAX(1,2.1)实例化中的两个数据的类型不同,第一个数据是int型,第二个数据是double型,直接调用,模板不能根据需要生成相应的函数
所以通过实例化MAX<int>(1,2.1>将double类型的数据转换成int型,就可以成功运行了。
隐式实例化:就是根据实例化的实参类型生成相应的类型函数,并调用过程,它的一般适用于参数列表实参类型都相同的数据。
函数模板特化:当函数模板需要对某些类型进行特别处理,称为函数模板的特化。
输出的结果是0,与原来比较两个字符串是否相等的结果不一致;对于传入的是char*类型的,而模板只是简单的简单的比较了传入参数的值,即两个指针是否相等,因此这里打印0。显然,这与我们的初衷不符,所以就需要对其进行实例化。
对模板函数进行特化如下:
template <>
bool IsEqual<const char*>(const char* t1, const char* t2) //函数模板特化
{
return strcmp(t1, t2);
}
模板函数特化形式如下:
1、关键字template后面接一对空的尖括号<>2、函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参
3、函数形参表
4、函数体
template<>
返回值 函数名<Type>(参数列表)
{
// 函数体
}