到底什么是template? 其重要性如何呢?下面我们先来了解一下这个在C++中很重要的性质。
很多时候,我们会碰到这样一种情况,一个函数或一段代码相同,但是我们使用的数据类型不同,那么我们经常需要复制一段既有的代码。举个例子,假设你写一个绘图函数,使用整数x,y坐标;突然之间你需要相同的程序代码,但坐标值改采用long。你当然可以使用一个文字编辑器把这段代码拷贝一份,然后把其中的数据类型改变过来。有了C++,你甚至可以使用重载函数,那么你就可以仍旧使用相同的函数名称。函数的重载的确使我们有比较清爽的程序代码,但它们意味着你还是必须在你的程序的许多地方维护完全相同的算法。
C语言对此问题的解答是:使用宏。虽然你因此对于相同的算法只需要写一次程序代码,但宏有它自己的缺点。第一,它只适用于简单的功能。第二个缺点比较严重:宏不提供数据类型检验,因此牺牲了C++的一个主要功能。第三个缺点是:宏并非函数,程序中任何调用宏的地方都会被编译器前置处理器原原本本地插入宏所定义的那一段代码,而非一个函数调用,因此你每使用一次宏,你的执行文件就会膨胀一点。
Templates提供了比较好的解决方案,它把“一般性的算法”和其“对数据类型的实作部分”区分开来。你可以先写算法的程序代码,稍后在使用时再填入实际数据类型。新的C++语法使“数据类型”以参数的姿态出现。有了template,你可以拥有宏“只写一次”的优点,以及重载函数“类型检验”的优点。
了解了template的用途,下面我们就来仔细的分析一下template这种C++特性。
C++的template有两种,一种针对function,另一种针对class。
1、Template Functions
让我们先从例子开始(本人最喜欢实例说明,感觉更容易理解,哈哈,原谅智商不够的我)。
假设需要一个计算数值幂次方的函数,名为power。我们只接受正幂次方数,如果是负幂次方,就让结果为0.
对于整数,函数应该是这样的:
int power( int base, int exponent)
{
int result = base;
if (exponent == 0) return (int)1;
if (exponent < 0) return (int)0;
while (--exponent) return *= base;
retun result;
}
对于长整型,函数应该是这样的:
long power( long base, int exponent)
{
long result = base;
if (exponent == 0) return (long)1;
if (exponent < 0) return (long)0;
while (--exponent) return *= base;
retun result;
}
同样,对于浮点数,对于复数,都可以有类似的函数。如果能够把数据类型也变成参数之一,在使用时指定,那岂不是变得简单的多了。是的,这就是template的妙用:
template <class T>
T power( T base, int exponent);
这样的函数声明是以一个特殊的template前缀开始,后面紧跟着一个参数列。class既可以是一个C++的class,也可以是一个普通的数据类型。
下面是power函数的template版本:
template <class T>
T power( T base, int exponent)
{
T result = base;
if (exponent == 0) return (T)1;
if (exponent < 0) return (T)0;
while (--exponent) return *= base;
retun result;
}
返回值必须确保为类型T,以吻合template函数的声明。
下面是template函数的调用方法:
#include <iostream>
void main()
{
int i = power(5 , 4);
long l = power( 1000L, 3);
long double d = power((long double)1e5, 2);
cout << "i = " << i <<endl;
cout << "l = " << l <<endl;
cout << "d = " << d <<endl;
}
执行结果:
i = 625
l = 1000000000
d = 1e+010
在第一次调用中,T变成int, 在第二次调用中,T变成long。而在第三次调用中,T又成为了一个long double。但是如果调用时把数据类型混乱掉了,像这样:
int i = power ( 1000L, 4); //基值是个long,返回值却是个int。编译时就会出错。
2、Template Classes
我们也可以建立template classes,使你建立的类可以神奇地操作任何类型的数据。下面这个例子是让CThree类储存三个成员变量,成员函数Min传回其中的最小值,成员函数Max则传回其中的最大值。我们把它设计成template classes,以便这个类能适用于各式各样的数据类型:
template <class T>
class CThree
{
private:
T a, b ,c;
public:
CThree( T t1, T t2 , T t3);
T Min();
T Max();
};
把T看成是大家熟悉的int或float也就是了。下面是成员函数的定义:
template <class T>
T CThree<T> :: Min()
{
T minab = a < b? a:b;
return minab < c? minab: c;
}
template <class T>
T CThree<T> :: Max()
{
T maxab = a < b? b:a;
return maxab < c? c: maxab;
}
template <class T>
CThree<T> :: CThree(T t1, T t2, T t3) :
a(t1), b(t2), c(t3)
{
return;
}
需要注意,每一个成员函数前都要加上 template <class T>, 而且类名称应该使用CThree<T>。
下面是定义的类的使用方法:
#include <iostream>
void main()
{
CThree<int> obj1( 2 , 5, 4);
cout << obj1.Min() << endl;
cout << obj1.Max() << endl;
CThree<float> obj2(8.52, -6.75, 4.54);
cout << obj2.Min() << endl;
cout << obj2.Max() << endl;
CThree<long> obj3(646600L, 437847L, 364873L);
cout << obj3.Min() << endl;
cout << obj3.Max() << endl;
}
执行结果:
2
5
-6.75
8.52
364873
646600