先看一个之前自定义的Stack类
// 别名
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item & item);
bool pop(Item & item);
};
模板类以下面这样的代码开头:
template
关键字template告诉编译器, 将要定义一个模板, 尖括号中的内容相当于函数的参数列表. 可以把关键字class看作是变量的类型名, 该变量接收类型作为其值, 把Type看做是该变量的名称.
这里的class并不意味着Type必须是一个类, 而只是表明Type是一个通用的类型说明符, 在使用模板时, 将使用实际的 类型替换它, 也可以使用typename替换class:
template
同样, 可以使用模板成员函数替换原有类的类方法. 每个函数头都将以相同的模板声明打头:
template
同样应使用泛型名Type(可以替换成T等其他字符)替换typedef标识符Item, 另外, 还需要将类限定符从Stack::改为Stack
bool Stack::push(const Item & item)
{
...
}
应改为:
template
bool Stack::push(const Type & item)
{
...
}
如果在类声明中定义了方法(内联定义), 则可以省略模板前缀和类限定符.
模板的具体实现--如:用来处理string对象的栈类被称为实例化(instantiation)或具体化(specialization). 不能将模板成员函数放在独立的实现文件中. 由于模板不是函数, 他们不能单独编译, 模板必须与特定的模板实例化请求一起使用. 因此最简单的方法是将所有模板信息放在一个头文件中, 并在要使用这些模板的肯建中包含该头文件:
看一个完整的例子:
// stacktp.h
#ifndef STACKTP_H_
#define STACKTP_H_
// 模板类
template
class Stack
{
private:
enum {MAX = 10};
Type items[MAX];
int top;
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & tiem);
bool pop(Type & item);
};
// 模板方法:
template
Stack::Stack()
{
top = 0;
}
template
bool Stack::isempty()
{
return top == 0;
}
template
bool Stack::isfull()
{
return top == MAX;
}
template
bool Stack::push(const Type & item)
{
if (top < MAX)
{
items[top++] = item;
return true;
} else {
return false;
}
}
template
bool Stack::pop(Type & item)
{
if(top > 0)
{
item = items[--top];
return true;
} else {
return false;
}
}
#endif
使用Stack模板类:
// 创建一个int型的Stack
Stack kernels;
// 创建一个string类型的Stack对象
Stack colonels;
调用模板类的文件:
// 使用Stack模板类
// stacktem.cpp
#include
#include
#include
#include "stacktp.h"
using std::cin;
using std::cout;
using std::endl;
int main()
{
Stack st;
char ch;
std::string po;
cout << "Please enter A to add a purchase order, " << endl;
cout << "P to process a PO, or Q to quit." << endl;
while(cin >> ch && std::toupper(ch) != 'Q')
{
while(cin.get() != '\n')
continue;
if(!std::isalpha(ch))
{
cout << '\a';
continue;
}
switch(ch)
{
case 'A':
case 'a':
cout << "Enter a PO number to add: ";
cin >> po;
if(st.isfull())
cout << "stack already full" << endl;
else
st.push(po);
break;
case 'p':
case 'P':
if(st.isempty())
cout << "stack already empty" << endl;
else {
st.pop(po);
cout << "PO #" << po << " popped" << endl;
}
break;
}
cout << "Please enter A to add a purchase order, P to process a PO, or Q to quit" << endl;
}
cout << "Bye" << endl;
return 0;
}
程序运行结果为:
允许指定数组大小的简单数组模板.
我们可以使用模板参数来提供常规数组的大写, 例如:
// arraytp.h
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include
#include
// 指出是一个模板类
// class指出T为类型参数, 而int指出n的类型为int, 这种参数(指定特殊的类型而不是用作泛型名, 例如这里的int)称为非类型或表达式参数.
template
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & v);
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
template
ArrayTP::ArrayTP(const T & v)
{
for(int i = 0; i < n; i++)
{
ar[i] = v;
}
}
template
T & ArrayTP::operator[](int i)
{
if(i < 0 || i >= n)
{
// 输出错误信息
std::cerr << "Error in array limits: " << i << " is out of range" << endl;
std::exit(EXIT_FAILURE);
}
return ar[i];
}
template
T ArrayTP::operator[](int i) const
{
if(i < 0 || i >= n)
{
// 输出错误信息
std::cerr << "Error in array limits: " << i << " is out of range" << endl;
std::exit(EXIT_FAILURE);
}
return ar[i];
}
#endif;
假设有如下的声明:
ArrayTP egg;
这将导致编译器定义名为ArrayTP
表达式参数可以使整型, 枚举, 引用或指针, 因此double m是不合法的, 但double * rm是合法的. 另外模板代码不能修改参数的值, 也不能使用参数的地址. 并且实例化模板时, 用作表达式参数的值必须是常量表达式.
表达式参数方法的主要缺点是, 每种数组大小都将生成自己的模板. 例如:
ArrayTP eggs;
ArrayTP donuts;
上面的声明将生成两个独立的类声明