list概述
相对于vector的连续性空间,list就显的复杂许多,它的好处是每次安插或删除一个元素,就配置或释放一个元素空间。因此,list对于空间的运用有绝对的精确,一点也不浪费。而且,对于任何职位的元素安插或元素移除,list永远是常数时间。
list和vector是两个最常使用的容器。什么时机下最合适使用哪一种容器,必须视元素的多寡,元素的构造复杂度,元素存取行为的特性而定。
list的节点
list本身和list的节点是不同的结构,需要分开设计,以下是STL list的节点node结构:
template <class T>
struct __list_node
{
typedef void* void_pointer;
void_pointer next;
void_pointer prev;
T data;
};
list的迭代器
list不在能够像vector一样以原生指标作为迭代器,因为其节点不保证在存储空间中连续存在。list迭代器必须有能力指向list的节点,并有能力做正确的递增,递减,取值,成员存取...等动作。STL list是一个双向串行,迭代器必须具备迁移,后移的能力。所以list提供的是Bidirectional Iterators。
list有一个重要性质:安插动作(insert)和接合动作(splice)都不会造成原有的list迭代器失效。这在vector是不成立的,因为vector的安插动作可能造成记忆体重新配置,导致原有迭代器全部失效。甚至list的元素删除动作erase,也只有(指向被删除元素)的那个迭代器失效,其它迭代器不受影响。
// 至于为什么不使用默认参数, 这个是因为有一些编译器不能提供推导能力,
// 而作者又不想维护两份代码, 故不使用默认参数
template<class T, class Ref, class Ptr>
struct __list_iterator
{
// 标记为'STL标准强制要求'的typedefs用于提供iterator_traits<I>支持
typedef __list_iterator<T, T&, T*> iterator; // STL标准强制要求
typedef __list_iterator<T, const T&, const T*> const_iterator;
typedef __list_iterator<T, Ref, Ptr> self;
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type; // STL标准强制要求
typedef Ptr pointer; // STL标准强制要求
typedef Ref reference; // STL标准强制要求
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type; // STL标准强制要求
// 这个是迭代器实际管理的资源指针
link_type node;
__list_iterator(link_type x) : node(x) {}
__list_iterator() {}
__list_iterator(const iterator& x) : node(x.node) {}
// 在STL算法中需要迭代器提供支持
bool operator==(const self& x) const { return node == x.node; }
bool operator!=(const self& x) const { return node != x.node; }
// 重载operator *, 返回实际维护的数据
reference operator*() const { return (*node).data; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
// 前缀自加
self& operator++()
{
node = (link_type)((*node).next);
return *this;
}
// 后缀自加, 需要先产生自身的一个副本, 然会再对自身操作, 最后返回副本
self operator++(int)
{
self tmp = *this;
++*this;
return tmp;
}
self& operator--()
{
node = (link_type)((*node).prev);
return *this;
}
self operator--(int)
{
self tmp = *this;
--*this;
return tmp;
}
};
list的数据结构
list不仅是一个双向串行,而且还是一个环状双向串行。所以它只需要一个指标,便可以完成表现整个串行。如果让指标node指向可以置于尾端的一个空白节点,node便能符合STL对于[前闭后开]区间的要求,成为last迭代器,如下图所示: