14.4.1 定义类模板
下面以第10章的Stack类为基础来建立模板。原来的类声明如下:
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Item items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty() const;
bool isfull() const;
// push() returns false if stack already is full, true otherwise
bool push(const Item & item); // add item to stack
// pop() returns false if stack already is empty, true otherwise
bool pop(Item & item); // pop top into item
};
采用模板时,将使用模板定义替换Stack声明,使用模板成员函数替换Stack的成员函数。和模板函数一样,模板类以下面这样的代码开头:
template <class Type>
关键字template告诉编译器,将要定义一个模板。尖括号中的内容相当于函数的参数列表。可以把关键字class看作是变量的类型名,该变量接受类型作为其值,把Type看作是改变量的名称。
这里使用class并不意味着Type必须是一个类;而只是表明Type是一个通用的类型说明符,在使用模板时,将使用实际的类型替换它。较新的C++实现允许在这种情况下使用不太容易混淆的关键字typename代替class:
template <typename Type> // new choice
可以使用自己的泛型名代替Type,其命名规则与其他标识符相同。当前比较流行的选项包括T和Type。在模板定义中,可以使用泛型名来标识要存储在栈中的类型。对于Stack来说,这意味着将声明中所有的typedef标识符Item替换为Type。例如,
Item items[MAX]; // holds stack items
应改为:
Type items[MAX]; // holds stack items
同样,可以使用模板成员函数替换原有类的类方法。每个函数头都将以相同的模板声明打头:
template <class Type>
同样应使用泛型名Type替换typedef标识符Item。另外,还需要将限定符从Stack::改为Stack<Type>::。例如,
bool Stack::push(const Item & item)
{
...
}
应改为:
template <class Type> // or template <typename Type>
bool Stack<Type>::push(const Type & item)
{
...
}
如果在类声明中定义了方法(内联定义),则可以省略模板前缀和类限定符。
程序清单14.13列出了类模板和成员函数。知道这些模板不是类和成员函数定义至关重要。它们是C++编译器指令,说明了如何生成类和成员函数定义。模板的具体是现——如用来处理string对象的栈类——被称为实例化(instantiation)或具体化(specialization)。不能将模板成员函数放在独立的实现文件中。由于模板不是函数,它们不能单独编译。模板必须与特定的模板实例化请求一起使用。为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该文件头。
程序清单14.13 stacktp.h
// stacktp.h -- a stack template #ifndef STACKTP_H_ #define STACKTP_H_ template <class Type> class Stack { private: enum {MAX = 10}; // constant specific to class Type items[MAX]; // holds stack items int top; // index for top stack item public: Stack(); bool isempty(); bool isfull(); bool push(const Type & item); // add item to stack bool pop(Type & item); // pop top into item }; template <class Type> Stack<Type>::Stack() { top = 0; } template <class Type> bool Stack<Type>::isempty() { return top == 0; } template <class Type> bool Stack<Type>::isfull() { return top == MAX; } template <class Type> bool Stack<Type>::push(const Type & item) { if (top < MAX) { items[top++] = item; return true; } else return false; } template <class Type> bool Stack<Type>::pop(Type & item) { if (top > 0) { item = items[--top]; return true; } else return false; } #endif // STACKTP_H_
14.4.2 使用模板类
仅在程序包含模板并不能生成模板类,而必须请求实例化。为此,需要生命一个类型为模板类的对象,方法是使用所需的具体类型替换泛型名。例如,下面的代码创建两个栈,一个用于存储int,另一个用于存储string对象:
Stack<int> kernels; // create a stack of ints
Stack<string> colonels; // create a stack of string objects
看到上述声明后,编译器将按Stack<Type>模板来生成两个独立的类声明和两组独立的类方法。
程序清单14.14修改了原来的栈测试程序(程序清单11.12),使用字符串而不是unsigned long值作为订单ID。
程序清单14.14 stacktem.cpp
// stacktem.cpp -- testing the template stack class #include <iostream> #include <string> #include <cctype> #include "stacktp.h" using std::cin; using std::cout; int main() { Stack<std::string> st; // create an empty stack char ch; std::string po; cout << "Please enter A to add a purchase order.\n" << "P to process a PO, or Q to quit.\n"; 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\n"; else st.push(po); break; case ‘P‘: case ‘p‘: if (st.isempty()) cout << "stack already empty\n"; else { st.pop(po); cout << "PO #" << po << " popped\n"; break; } } cout << "Please enter A to add a purchase order.\n" << "P to process a PO, or Q to quit.\n"; } cout << "Bye\n"; return 0; }
效果:
Please enter A to add a purchase order. P to process a PO, or Q to quit. A Enter a PO number to add: moonlightpoet Please enter A to add a purchase order. P to process a PO, or Q to quit. A Enter a PO number to add: moonlit Please enter A to add a purchase order. P to process a PO, or Q to quit. P PO #moonlit popped Please enter A to add a purchase order. P to process a PO, or Q to quit. P PO #moonlightpoet popped Please enter A to add a purchase order. P to process a PO, or Q to quit. P stack already empty Please enter A to add a purchase order. P to process a PO, or Q to quit. Q Bye