我们在使用STL时,包含的头文件是<stack.h>,而stack中并没有实体,只是“简单”包含了<_stack.h>,真正的东西,在这个头文件中。
一、stack的三种模板
#if !defined ( _STLP_LIMITED_DEFAULT_TEMPLATES )
template <class _Tp, class _Sequence = deque<_Tp> >
#elif defined ( _STLP_MINIMUM_DEFAULT_TEMPLATE_PARAMS )
# define _STLP_STACK_ARGS _Tp
template <class _Tp>
#else
template <class _Tp, class _Sequence>
#endif
stack模板类有三种模板。一般情况下我们不会定义_STLP_LIMITED_DEFAULT_TEMPLATES(如果定义了则表示不使用默认模板),默认模板是:
template <class _Tp, class _Sequence = deque<_Tp> >
可见,stack的内部实现实际上是使用deque(双端队列)。
二、起别名
#ifdef _STLP_STACK_ARGS
typedef deque<_Tp> _Sequence;
typedef stack<_Tp> _Self;
#else
typedef stack<_Tp, _Sequence> _Self;
#endif
根据情况_Self。
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
由此可见,_Suquence必须有如下成员(例如Deque就有此四个成员):
value_type;
size_type;
reference;
const_reference。
三、成员变量
_Sequence c;
只有一个成员变量,来记录容器的元素。这里仍是deque。
四、栈的方法
1. 判断栈是否为空
bool empty() const { return c.empty(); }
2. 栈的大小
size_type size() const { return c.size(); }
3. 访问栈顶元素
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
4. 出栈
void pop() { c.pop_back(); }
5. 入栈
void push(const value_type& __x) { c.push_back(__x); }
6. 得到整个内部容器,即得到双向队列(并非栈的必须方法)
const _Sequence& _Get_s() const { return c; }
7. 交换两个栈的内部容器(并非栈的必须方法)
#if defined (_STLP_USE_PARTIAL_SPEC_WORKAROUND) && !defined (_STLP_FUNCTION_TMPL_PARTIAL_ORDER)
void _M_swap_workaround(_Self& __x) {
_Sequence __tmp = c;
c = __x.c;
__x.c = __tmp;
}
#endif
五、重载操作符(针对栈的)
1. 定义“方便”宏
#ifndef _STLP_STACK_ARGS
# define _STLP_STACK_ARGS _Tp, _Sequence
# define _STLP_STACK_HEADER_ARGS class _Tp, class _Sequence
#else
# define _STLP_STACK_HEADER_ARGS class _Tp
#endif
2. 重载<操作符
template < _STLP_STACK_HEADER_ARGS >
inline bool _STLP_CALL operator<(const stack< _STLP_STACK_ARGS >& __x,
const stack< _STLP_STACK_ARGS >& __y)
{ return __x._Get_s() < __y._Get_s(); }
注意几点(此后不再说明):
(1) 声明为inline。因为函数较短,适用(内联会在任何被调用的地方展开);
(2) 参数声明为常量引用。因为传入的参数可能很大,如果普通传参会复制个副本,浪费内存。
3. 重载==操作符
template < _STLP_STACK_HEADER_ARGS >
inline bool _STLP_CALL operator==(const stack< _STLP_STACK_ARGS >& __x,
const stack< _STLP_STACK_ARGS >& __y)
{ return __x._Get_s() == __y._Get_s(); }
比较操作符都依赖于Sequence(即deque)的比较操作符。
六、综上所述,stack实际上并没有多少自已的逻辑,几乎完全依赖于deque。在实现上,好像也可能用stack作为deque的子类来实现,为什么不这样而采用模板的形式呢?可能是因为,deque还提供了许多stack并不需要的方法,这违背面向对象的原则,stack并非是deque的子类(没有deque的全部特性)。