C++ Template
一、Template
什么是template?重要性如何?下面我就说道说道:
无性生殖不只是存在于遗传工程中,对于程序员而言,它也是一个由来已久的动作。过去,我们只不过是以一个简单而基本的工具,也就是一个文字编辑器,重复的复制代码。今天,C++提供给我们一个更好的繁殖方法:template。
复制一段既有的代码,最常见的一个理由是:为了改变数据类型,举个例子,假设你写了一个绘图函数,使用类型为long的X,Y坐标;突然之间你需要相同的算法,但坐标值改成float。你当然可以使用一个文字编辑器把这段代码拷贝一份,然后改变其中的数据类型。你甚至可以使用C++的overloade functions,那么就可以继续使用相同的函数名称。Function Overloading的确使用有比较清爽的程序码,但你还是必须在程序的许多的地方维护完全相同的演算法。
C语言对此问题的解决之道是:使用macros。但macros有它自已的缺点,第一,它只适用于简单的功能。第二个缺点比较严重:macros不提供参数类型检验,这与C++严格的类型检验格格不入。第三个缺点是:macro并非functions,程序中任何呼叫macros的地方都会被编译器前置处理原原本本插入macros,你的执行档就会膨胀一些。
Template提供比较好的解决方案,它把“一般性的演算法”和其“对资料类型的实际联结”区分开,你可以先写出演算法的代码,使用时才填入实际数据的类型。C++ Template使“数据类型”也以参数的姿态出现。有了template,我们就可以拥有macros“只写一次”的优点,以及overloaded functions“类型检验”的优点。
Template的好处其实不仅于此,你可以利用template设计出一般化(泛型)的演算法,适用于“目前存在”以及“尚未被设计出来”的某种数据类型(某种class type)。当然,如果要让新开发的class type能够适用于此泛型演算法,那些class在设计时也必须配合某些事情。
二、Template function
假设我需要一个幂次方计算函数,叫power()。这个函数只接受正幂次方数,如果是负幂次方数,就让结果为零。
对于int数据,power()函数应该是这样的:
1 int power(int base, int exponent) 2 { 3 int result = base; 4 if (exponent==0) return(int) 1; 5 if (exponent<0) return (int) 0; 6 while(--exponent) result *=base; 7 return result; 8 }
对于long数据,power()函数应该是这样:
1 long power(long base, int exponent) 2 { 3 int result = base; 4 if (exponent==0) return(long) 1; 5 if (exponent<0) return (long) 0; 6 while(--exponent) result *=base; 7 return result; 8 }
对于float我们应该.....,对于complex我们又应该.....,哦,为什么不能够把数据类型也变成参数之一,在呼叫函数时加以指定呢?是的,这个就是Template的妙用,语法如下:
template<class T> T power(T base, int exponent);
写成两行或许比较清楚:
template <class T>
T power(T base,int exponent);
这样的函数声明是以一个特殊的template字首开始,后面紧跟着一个参数列(本例只有一个参数T)。容易让人迷惑的是其中class字眼,它并不一定表示C++的calss,它可以只是一个普通的(内建的)的数据类型。<class T>的意思是:T是一种数据类型,此类型将在调用此函数时才由调用者指定。
下面就是power()的template版本,注意传回值也必须吻合template函数的声明:
1 template <class T> 2 T power(T base, int exponent) 3 { 4 T result=base; 5 if (exponent ==0) return (T)1; 6 if (exponent <0) return(T) 0; 7 while (--exponent) result *=base; 8 return result; 9 }
下面是template函数的调用方法:
1 int i =power(5,4); 2 long l=power(1000L,3); 3 long double d=power((long double)1e5,2);
在第一次调用中我们指定T为int,第二次调用中我们指定T为long。而在第三次调用中T又成为一个long double。这些template function都可以应对;
三、Template Class
除了template functions,我们还可以建立template classes,使它们能够神奇地操作任何类型的数据。下面这个例子,CThree内含三个data members,Min()传回其中的最小值,Max()传回其中的最大值:
template <class T> class CThree { public: CThree(T t1,T t2,T t3); T Min(); T Max(); private: T a,b,c; };
请把T看成熟悉的int或float,语法就不至于太稀奇了,以下是上述三个member functions的定义:
1 template <class T> 2 class CThree 3 { 4 public: 5 CThree(T t1,T t2,T t3); 6 T Min(); 7 T Max(); 8 private: 9 T a,b,c; 10 }; 11 12 template <class T> 13 T CThree<T>::Min() 14 { 15 T minab =a<b?a:b; 16 return minab<c?minab:c; 17 } 18 19 template <class T> 20 T CThree<T>::Max() 21 { 22 T minab =a<b?b:a; 23 return minab<c?c:minab; 24 } 25 26 template <class T> 27 T CThree<T>::CThree(T t1,T t2,T t3): 28 a(t1),b(t2),c(t3) 29 { 30 return; 31 }
请注意,每个member function都要在最前面加上template<class T>,而且class 名称应该使用CThree<T>,不是CTree。
以下是template class的使用方法:
1 #include <iostream.h> 2 void main() 3 { 4 CThree<int> obj1(2, 5, 4); 5 cout << obj1.Min() << endl; 6 cout << obj1.Max() << endl; 7 CThree<float> obj2(8.52, -6.75, 4.54); 8 cout << obj2.Min() << endl; 9 cout << obj2.Max() << endl; 10 CThree<long> obj3(646600L, 437847L, 364873L); 11 cout << obj3.Min() << endl; 12 cout << obj3.Max() << endl; 13 }
执行结果一切正确:
2 5 -6.75 8.52 364873 646600