2.2 实践——栈类模板
例2.1中只是定义一个复合数据结构,并没有涉及成员函数等。而类模板与普通类一样,可以有各种成员函数、继承、运算符重载等。为了使例子更加丰满一些,我们来设计一个虽然简陋但功能完整的栈类模板。
2.2.1 栈类模板实例
首先,要有一个栈类来封装数据入栈/出栈的操作,并且可以报告栈是否为空。为使其可用于多种数据类型,该栈类显然也应该写成一个类模板。这里将该栈类模板命名为my_stack。
其次,例2.1的栈节点类模板list_node应当只为栈类模板所用。为简化代码,可以将list_node所有成员完全私有,但声明my_stack为友元类,使其可以访问list_node的私有成员。my_stack负责组织由list_node组成的单向链表。这样一个栈类模板代码如例2.2所示。
例2.2
-
- // -----------------------------
- // 文件名:stack.hpp
- #include <stdexcept>
-
- template<typename T> class my_stack; // 前置栈类模板声明
-
- template<typename T>
- class list_node
- {
- T value;
- list_node *next;
-
- // 私有构造函数,只能由其友类构造
- list_node(T const &v, list_node *n) :
- value(v), next(n) {}
-
- // 友类必须是类模板my_stack的实例
- friend class my_stack<T>;
- };
-
- template<typename T=int>
- class my_stack
- {
- typedef list_node<T> node_type;
- node_type *head;
-
- // my_stack不可复制构造,也不可赋值
- my_stack operator=(my_stack const &) {}
- my_stack(my_stack const &s) {}
-
- public:
- // 构造与析构
- my_stack() : head(0) {}
- ~my_stack() {while (!empty()) pop();}
-
- // 在类模板内实现的成员函数模板
- bool empty() const {return head == 0;}
- T const& top() const throw (std::runtime_error) {
- if (empty())
- throw std::runtime_error("stack is empty.");
- return head->value;
- }
- void push(T const &v) {head = new node_type(v, head);}
-
- // 成员函数声明,将在类模板外实现
- void pop();
- };
-
- // 在类模板外实现的成员函数模板
- template<typename T>
- void my_stack<T>::pop()
- {
- if (head) {
- node_type *tmp = head;
- headhead = head->next;
- delete tmp;
- }
- }
例2.2中首先是类模板my_stack的前置声明。有此前置声明,编译器已知my_stack为类模板且需要一个模板参数,则认可之后遇到的my_stack<T>为合法声明,否则将报错。
类模板list_node内容不变,只是所有成员都是私有,并且声明my_stack<T>为其友类,以便my_stack直接访问其成员。接下来是一个略微复杂的my_stack类模板。在类模板内首先用typedef为list_node<T>赋一个别名node_type。这样在随后用到list_node模板时就无须一一给出实参。在需要用到大量模板实例类型时,这样做可以使得代码看起来更简洁。在以后的例子中还会看到,typedef在模板编程(www.cppentry.com)中的作用远不止于使代码整洁这么简单。
my_stack中只有一个成员变量,即由node_type组成的单向链表的表头指针。鉴于该指针是独占指针,即其所指内容不可与其他指针共享,为防止对栈进行复制构造或者赋值操作时破坏其独占权,最简单的处理方式是将复制构造函数及赋值操作符声明为私有,以禁止用户调用。
随后即为my_stack类模板的公开构造、析构函数和若干成员函数。函数本身并无新意,重点在于模板中成员函数的写法。既然my_stack是一个类模板,其成员函数也天然是模板,并且继承了类模板的参数。在C++(www.cppentry.com)中,类成员函数的实现代码既可以直接写在类实现体内,也可以单独写在类实现体外。对于第一种方式,类模板的成员函数与普通类的成员函数的写法并无多少差异,如例2.2中my_stack的成员函数top()。而对于第二种方式,由于类模板成员函数本身也是模板,单独实现时也需要写成模板。已知普通类成员函数在外部实现时,代码如下:
- return_value class_name::func_name(param_list) {/*函数实现体*/}
对于类模板成员函数来说,class_name本身是一个模板名,则需要写成如下所示:
- template<T>
- return_value class_name<T>::func_name(param_list) {/*函数实现*/}
例2.2中my_stack的成员函数pop()即是如此。