slist概述
slist是一个单向链表,但其实现其实比list要更复杂。slist不在标准规则之内。
对于STL习惯来说,插入操作往往是在指定位置前插入,这对slist来说效率太低,因此只提供了insert_after()和erase_after()。
由于slist不方便插入和删除,其不提供push_back(),只提供push_front()。
节点
slist的节点和迭代器的设计,比list复杂很多,运用了继承机制,因此在类型转换上表现地更为复杂。
迭代器相关代码:
struct _Slist_iterator_base
{
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef forward_iterator_tag iterator_category;
_Slist_node_base* _M_node; // 指向slist的节点
_Slist_iterator_base(_Slist_node_base* __x) : _M_node(__x) {}
void _M_incr() { _M_node = _M_node->_M_next; }
// 这两个函数在遍历是会用到,两个迭代器是否相等,就看其节点是否相等
// 节点并未重载 ==,也就是指向同一个节点才相等
bool operator==(const _Slist_iterator_base& __x) const {
return _M_node == __x._M_node;
}
bool operator!=(const _Slist_iterator_base& __x) const {
return _M_node != __x._M_node;
}
};
template <class _Tp, class _Ref, class _Ptr>
struct _Slist_iterator : public _Slist_iterator_base
{
typedef _Slist_iterator<_Tp, _Tp&, _Tp*> iterator;
typedef _Slist_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
typedef _Slist_iterator<_Tp, _Ref, _Ptr> _Self;
typedef _Tp value_type;
typedef _Ptr pointer;
typedef _Ref reference;
typedef _Slist_node<_Tp> _Node;
......
// 不提供 --的运算符重载,不允许后退
};
slist遍历
这里就是会用到 重载符 ==,迭代器==的判断其实就是slist_node_base的比较。
对于end()
iterator end() { return iterator(0); }
构建一个临时迭代器对象,同时生成一个_slist_node_base类的临时节点node(0),
对于单链表最后一个节点,其next值本身也指向_slist_node_base类的node(0)
_Node* _M_create_node(const value_type& __x) {
_Node* __node = this->_M_get_node();
__STL_TRY {
construct(&__node->_M_data, __x);
__node->_M_next = 0;
}
有点困惑,虽然node里都只有一个值next,且next的值相同,但两个node(0)的地址不同,_slist_node_base又没重载 ==,应该是不相等啊。
侯捷老师的书上也说明了这点
不过这里也体现出继承的一个好处吧,next和data分离,能更简单地判断当前迭代器是不是end()