返回完整目录html
正如函数模板,能够以下方式在一个头文件中声明和定义类Stack<>
:安全
// basics/stack1.hpp #include <vector> #include <cassert> template <typename T> class Stack { private: std::vector<T> elems; //元素 public: void push(T const& elem); //压入元素 void pop(); // 弹出元素 T const& top() const; //返回顶上的元素 bool empty() //返回栈stack是否为空 { return elems.empty(); } }; template <typename T> void Stack<T>::push(T const& elem) { elems.push_back(elem); //将elem的拷贝放入elems末尾 } template <typename T> void Stack<T>::pop() { assert(!elems.empty()); elems.pop_back(); //移除最后一个元素 } template <typename T> T const& Stack<T>::top() const { assert(!elems.empty()); return elems.back(); //返回最后一个元素 }
正如所看到的,该类模板使用C++标准库的vector<>
实现,这样便无需实现内存管理、拷贝控制和赋值运算,这样即可以将重心放在类模板的接口上。ide
声明类模板与声明函数模板相似:在声明以前,必须声明一个或多个类型参数的标识符。再一次,T是一个经常使用的标识符:函数
template <typename T> class Stack { ... };
此处的关键字typename
也能够用class
代替:spa
template <class T> class Stack { ... };
在类模板中,T能够像其余任何类型同样用于声明成员和成员函数(member function)。该例子中,T用于声明成员的类型为T的向量vector
push()
使用T类型做为参数,用于声明成员函数
top()
的返回类型。
template <typename T> class Stack { private: std::vector<T> elems; //元素 public: void push(T const& elem); //压入元素 void pop(); // 弹出元素 T const& top() const; //返回顶上的元素 bool empty() //返回栈stack是否为空 { return elems.empty(); } };
该类的类型为Stack
好比,若是必须声明构造函数和赋值运算符,这一般看起来像这样:blog
template <typename T> class Stack { ... Stack(Stack const&); //拷贝构造 Stack& operator=(Stack const&); //赋值运算符 };
这与以下形式等价:接口
template <typename T> class Stack { ... Stack(Stack<T> const&); //拷贝赋值 Stack<T>& operator=(Stack<T> const&); //赋值运算符 };
但一般
然而,在类结构以外须要指定模板参数:
template <typename T> bool operator==(Stack<T> const& lhs, Stack<T> const& rhs);
注意到在须要类名而不是类的类型的地方,仅仅使用Stack即可以。这特别是在构造函数和析构函数名字的情形中。
与非模板类不一样,不能在函数内部或者块做用域(block scope)
内声明类模板。一般,模板只能定义在全局做用域(global scope)或者命名空间做用域(namespace scope)或者类声明内(详见12.1节)。
定义类模板的成员函数必须指定这是一个模板且必须使用类模板的完整类型限制。所以,类型Stack
push()
的实现为
template <typename T> void Stack<T>::push(T const& elem) { elems.push_back(elem); //将elem的拷贝放入elems末尾 }
该状况下,成员向量的push_bask()
方法被调用,将元素放入向量vector的末尾。
注意到向量vector的pop_back()
将移除最后一个元素但不返回,这行为的缘由是异常安全(exception safety)。不可能实现一个返回移除元素的彻底异常安全的pop()
版本(该问题首先由Tom Cargill在[CargilExceptionSafety]中的第10项条款[SutterExceptional]中讨论)。然而,若是忽略该危险,能够实现返回移除的元素的pop()
。为实现此功能,简单地使用T来声明类型为元素类型的局部变量:
template <typename T> T Stack<T>::pop() { assert(!elems.empty()); T elem = elems.back(); //保存最后一个元素 elems.pop_back(); //移除最后一个元素 return elem; //返回保存元素的拷贝 }
因为当vector中没有元素时,back()
(返回最后一个元素)和pop_back()
(移除最后一个元素)都将有未定义的行为,所以须要检查栈是否为空。若是为空,则断言(assert),由于在空的栈上调用pop()
是错误的。在top()
上也须要这么作,它返回顶上的元素但不移除:
template <typename T> T const& Stack<T>::top() const { assert(elems.empty()); return elems.back(); //返回最后一个元素 }
固然,对于任何成员函数,也能够将类模板的成员函数在类的声明中实现为inline,好比:
template <typename T> class Stack { ... void push(T const& elem) { elems.push_back(elem); //将elem放入末尾 } };