与函数模板相似地(C++模板详解(一):函数模板的概念和特性) ,类也能够被一种或多种类型参数化。例如,容器类就是一个具备这种特性的典型例子,它一般被用于管理某种特定类型的元素。只要使用类模板,咱们就能够实现容器类,而不须要肯定容器中元素的类型。html
在这篇博文中,咱们使用Stack
做为类模板的例子。函数
#include <vector> #include <deque> #include <stdexcept> template<typename T> class Stack { std::vector<T> elems; // 存储元素的容器。 public: void push(const T& value); // 将元素压入栈中。 void pop(); // 将栈顶元素弹出栈。 T top() const; // 查看栈顶元素的副本。 bool empty() const { // 检查栈是否为空。 return elems.empty(); } }; template<typename T> void Stack<T>::push(const T& value) { elems.push_back(value); } template<typename T> void Stack<T>::pop() { if (elems.empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } elems.pop_back(); } template<typename T> T Stack<T>::top() const { if (elems.empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } return elems.back(); }
如上所示,类模板的声明和函数模板的声明很类似:在声明以前,咱们先声明参数类型的标识符code
template<typename /*class*/ T> class Stack { //... };
固然,也可使用关键字class
来代替typename
。在类模板的内部,类型T
能够像其它的类型同样,用于声明成员变量和成员函数。在这个例子中,类的类型是Stack<T>
,其中T
是模板参数。所以,当在声明中须要使用该类的类型时,咱们必需要使用Stack<T>
。例如,若是要声明本身实现的拷贝构造函数和赋值运算符,那就应该这样来编写:htm
template<typename T> class Stack { //... Stack(const Stack<T>& other); // 这里的构造函数名称须要与类名相同(Stack) Stack<T>& operator=(const Stack<T>& other); // 这里的拷贝构造函数须要使用类的类型(Stack<T>) //... };
然而, 当须要使用类名而不是类的类型时,就应该只用Stack
。例如,当指定类的名称,或是须要编写构造函数、析构函数时,就须要使用Stack
。blog
为了定义类模板的成员函数,咱们必需要指定该成员函数是一个函数模板(使用template<typename T>
),并且还须要使用这个类模板的完整类型限定运算符Stack<T>::
。所以,成员函数push
的完整定义以下:get
template<typename T> void Stack<T>::push(const T& value) { elems.push_back(value); }
其它成员函数的实现也是相似的;和普通类定义相同,彻底也能够将成员函数的实现内联地写在类中,例如:string
template<typename T> class Stack { std::vector<T> elems; // 存储元素的容器。 public: // ... bool empty() const { // 检查栈是否为空。 return elems.empty(); } };
参见以下的main
函数代码:io
int main() { try { Stack<int> intStack; Stack<std::string> stringStack; // 使用int栈 intStack.push(7); std::cout << intStack.top() << std::endl; // 使用string栈 stringStack.push("hello"); std::cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (std::exception &ex) { std::cerr << "Exception: " << ex.what() << std::endl; return EXIT_FAILURE; } return 0; }
注意:只有那些被调用了的成员函数,才会产生这些函数的实例化代码。模板
因此,针对这个类模板,缺省的构造函数、push
、top
方法都针对int
、std::string
进行了实例化。然而, pop
方法只提供了std::string
的实例化。这样作的好处是:class
另外一方面,若是类中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员。