模板是C++中泛型编程的基础。一个模板就是一个创建类或函数的公式,使用模板能够让程序员编写与类型无关的代码。
假定我们希望编写一个函数来比较两个值的大小,由于C++语言定义函数或变量时都必须确定其数据类型,因此对于不同的数据类型,比较函数都必须定义一份,以int、double和string为例:
int compare(const int &v1, const int &v2)
{
if (v1 < v2) return -1;
else if (v2 < v1) return 1;
return 0;
}
int compare(const double &v1, const double &v2)
{
if (v1 < v2) return -1;
else if (v2 < v1) return 1;
return 0;
}
int compare(const string &v1, const string &v2)
{
if (v1 < v2) return -1;
else if (v2 < v1) return 1;
return 0;
}
上述通过定义多个重载函数实现大小比较的方式可看出,每个函数几乎是相同的,唯一的差异是参数的类型,函数体则完全一样。如果希望对每种数据类型数据大小比较不再重复定义与上述三种类型完全一样的函数体,我们就需要使用函数模板,这样就不需要为每种数据类型都定义一个新函数了。对于上述compare函数,其函数模板可定义为:
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
else if (v2 < v1) return 1;
return 0;
}
模板定义以关键字template开始,后跟一个模板参数列表(逗号分隔的一个或多个模板参数),关键字typename可用class代替,不过通常建议优先选用typename。
下面编写一个类似标准库find算法的模板,需要两个模板类型参数,一个表示迭代器参数,另一个表示数据类型参数,其模板定义如下:
template <typename InputIt, typename T>
InputIt find(InputIt begin, InputIt end, const T& value)
{
for (; begin != end; ++begin)
{
if (value == *begin)
{
return begin;
}
}
return end;
}
对于上述compare和find函数,其调用方式可参考如下:
int main() {
cout << compare(1, 2) << endl;
cout << compare(3.5, 2.7) << endl;
vector<int> intVector;
intVector.push_back(1);
intVector.push_back(0);
vector<int>::iterator it = find(intVector.begin(), intVector.end(), 0);
if (intVector.end() == it)
{
cout << "find nothing" << endl;
}
else
{
int n = it - intVector.begin();
cout << "find it, it=" << n << endl;
}
system("pause");
return 0;
}
类模板可用于表示一组类,这些类仅仅是成员变量类型不同,但对成员变量的操作类似。
下面以Stack为例进行类模板的演示,使用模板参数T声明成员变量,栈中可存储各种数据类型,不需要为每一种数据类型都写一个这样的类。
template <typename T>
class Stack{
public:
Stack(int s = 10);
~Stack();
int push(const T&); //入栈(添加元素)
int pop(T&); //出栈(删除元素)
int isEmpty() const;
int isFull() const;
void printAll();
private:
int size; //栈大小
int top; //顶部元素位置
T* stackPtr; //存储数据指针
};
//模板类的每一个成员函数都是模板函数,需要在没一个函数实现前加模板参数声明
template <typename T>
Stack<T>::Stack(int s)
:size(s), top(-1), stackPtr(NULL)
{
stackPtr = new T[size];
}
template <typename T>
Stack<T>::~Stack()
{
delete []stackPtr;
stackPtr = NULL;
}
template <typename T>
int Stack<T>::push(const T& value)
{
if (isFull())
return -1;
stackPtr[++top] = value;
return 0;
}
template <typename T>
int Stack<T>::pop(T& value)
{
if (isEmpty())
return -1;
value = stackPtr[top--];
return 0;
}
template <typename T>
int Stack<T>::isEmpty() const
{
return -1 == top;
}
template <typename T>
int Stack<T>::isFull() const
{
return size - 1 == top;
}
template <typename T>
void Stack<T>::printAll()
{
if (isEmpty())
{
cout << "no element in Stack" << endl;
return;
}
for (int i = 0; i <= top; ++i)
cout << stackPtr[i] << " ";
cout << endl;
}
类似函数模板,类模板以关键字template开始,后跟模板参数列表。在类模板(及其成员)的定义中,我们将模板参数当做替身,代替使用模板时用户需要提供的类型或值。
与函数模板不同的是,编译器不能为类模板推断模板参数类型,为了使用类模板,必须在模板名后的尖括号中提供type信息—用来代替模板参数的模板实参列表。调用Stack模板类的方法参考如下:
int main() {
//类模板在声明该模板的对象时,必须明确对模板参数实例化
Stack<int> stack_int; //实例化T为int类型
stack_int.push(12);
stack_int.push(34);
cout << "push 12 and 34, printAll:" << endl;
stack_int.printAll();
int popEle = -1;
stack_int.pop(popEle);
cout << "pop one = " << popEle << endl;
cout << "printAll:" << endl;
stack_int.printAll();
stack_int.pop(popEle);
cout << "pop one = " << popEle << endl;
cout << "printAll:" << endl;
stack_int.printAll();
system("pause");
return 0;
}