因为使用模板可以使程序员建立具有通用类型的函数库和类库。模板也是C++语言支持参数化多态性的工具。
函数模板
函数模板能以同样的程序代码对不同类型的数据进行处理,其关键是将所处理的数据类型说明为参数,即类型参数化。
定义函数模板的一般形式是:
template <class 类型参数名1 ,class 类型参数名 2,…>
函数返回值类型 函数名(形参表)
{
函数体
}
说明:
(1)这里的类型参数名就是代表形形色色数据类型的通用参数名,它可以代表基本数据类型,也可以代表类。
(2)编写函数模板的方法是:
step 1:定义一个普通的函数,数据类型采用具体的普通的数据类型,仍以打印数组为例,先定义一个打印整型数组的普通函数:
void printArray(int *array,int count)
{
for (int i=0;i<count;i++)
cout<<array[i]<<" ";
cout <<endl;
}
step 2:将数据类型参数化:将其中具体的数据类型名(如 int )全部替换成由自己定义的抽象的类型参数名(如T)。
step 3:在函数头前用关键字template引出对类型参数名的声明。这样就把一个具体的函数改造成一个通用的函数模板:
template <class T> void printArray(T *array,int count)
{
for (int i=0;i<count;i++)
cout<<array[i]<<" ";
}
(3)函数模板不是一个可以执行的函数,它只是对函数功能的程序描述,编译程序不为它生成执行代码。
(4)当编译程序遇到函数调用:
函数名 (实参表)时,自动将实参表中具体的数据类型替换函数模板中的类型参数,生成一个隐含的重载函数,该重载函数的程序代码与函数模板相同,类型则采用实参表中的具体数据类型。仍以前面所创建的打印数组函数模板为例,当程序出现函数调用语句printArray(a,aCount);时,编译程序将根据第一个实参a的数据类型int 匹配类型参数T,将函数模板中的所有T都替换成具体的数据类型int,从而产生一个具体的可执行函数的定义(这个过程称为模板实例化):
void printArray(int *array,int count);
模板函数
函数模板是对一组函数的描述,它不是一个实实在在的函数,编译系统并不产生任何执行代码。当编译系统在程序中发现有与函数模板中相匹配的函数调用时,便生成一个重载函数,该重载函的函数体与函数模板的函数体相同。该重载函数称为模板函数。
类模板
类模板概念的由来:列举一个链表类来讨论类模板引进的必要性。下面有一个List类是链表类,嵌套了一个Node结构类型。具体定义如下:
class List { //定义链表类 public: List(); //声明构造函数 void Add(int&); //声明增加节点的成员函数 void Remove(int&); //声明删除节点的成员函数 int* Find(int&); //声明查找节点的成员函数 void PrintList(); //声明打印链表的成员函数 ~List(); //声明析构函数 protected: struct Node { //定义结点的结构类型 Node *pNext; //指向下一个结点的指针成员 int* pT; //指向本结点数据的指针成员 } };
该链表结点的数据类型是int型,如果要使链表中结点的数据是其它类型(如char型、double型),就要重新定义链表类,由于这些类仅仅只是结点的数据类型不同,其余代码完全相同,因此造成程序代码大量重复。为链表类家族创建一个通用链表类模板就能最大限度地减少程序代码的冗余,为此引进类模板。将上述的List类中的数据类型用一个通用参数T来代替,如下所示:
template<class T> class List { //定义通用链表类模板 public: List(); //声明构造函数 void Add(T&); //声明增加节点的成员函数 void Remove(T&); //声明删除节点的成员函数 T* Find(T&); //声明查找节点的成员函数 void PrintList(); //声明打印链表的成员函数 ~List(); //声明析构函数 protected: struct Node{ //定义结点的结构类型 Node *pNext; //指向下一个结点的指针成员 T* pT; //指向本结点数据的指针成员 }; };
这个类模板中使用了参数类型T,它被用在成员函数Add()、Remove()和Find()的说明中以及结构体的定义中。实际上,一个类模板是描述了一组类。可见,引进类模板也同样是为了减少程序员的重复劳动,克服程序冗余的一种方法。
如同函数模板一样,使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值能取任意类型。类模板是对一批仅仅成员数据类型不同的类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,给出一套程序代码,就可以用来生成多种具体的类,(这类可以看作是类模板的实例),从而大大提高编程的效率。
定义类模板的一般形式是:
template <类型名 参数名1,类型名 参数名2,…> class 类名 { 类声明体 }; 例如, template <class T> class Smemory { … public: void mput(T x); … }
表示定义一个名为Smemory的类模板,其中带类型参数T。
在类模板的外部定义类成员函数的一般形式是:
template <类型名 参数名1,类型名 参数名2,…> 函数返回值类型 类名<参数名 1 参数名 2,…>::成员函数名(形参表) { 函数体 } 例如: template <class T> void Smemory<T>::mput(T x) {…}
表示定义一个类模板Smemory的成员函数,函数名为mput,形参x的类型是T,函数无返回值。
类模板是一个类家族的抽象,它只是对类的描述,编译程序不为类模板(包括成员函数定义)创建程序代码,但是通过对类模板的实例化可以生成一个具体的类以及该具体类的对象。
与函数模板不同的是:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定,
其实例化的一般形式是:
类名 <数据类型 1(或数据),数据类型 2(或数据)…> 对象名
例如,Smemory<int> mol;表示将类模板Smemory的类型参数T全部替换成int 型,从而创建一个具体的类,并生成该具体类的一个对象mol。