List简述:
List和Vector都是STL的序列式容器,唯一不同的地方就在于:Vector是一段连续的内存空间,List则是一段不连续的内存空间,相比于Vector来说,List在每次插入和删除的时候,只需要配置或释放一个元素空间,对于任何位置的插入和删除操作,List永远能做到常数时间。但是,List由于不连续的内存空间,导致不支持随机寻址,所以尺有所长寸有所短,在程序中选择使用那种容器还要视元素的构造复杂度和存取行为而定。
- List的节点
-
List的节点结构如下:
template <class T>
struct __list_node
{
typedef void* void_pointer;
void_pointer next; //型别为void*,也可以设为__list_node<T>*
void_pointer prev;
T data;
};
从节点结构可以看出,List就是一个双向链表.
- List的迭代器
在Vector中,由于是连续的存储空间,支持随机存取,所以其迭代器可以直接用普通指针代替。但是,在List中行不通。List必须有能力指向List的节点,并有能力进行正确的递增、递减、取值和成员存取等操作。List是一个双向链表,迭代器必须具备前移、后退的能力,所以List的迭代器是一个Bidirectional Iterator!在Vector中如果进行插入和删除操作后迭代器会失效,List有一个重要的性质就是插入和接合操作都不会造成原有的List迭代器失效。而且,再删除一个节点时,也仅有指向被删除元素的那个迭代器失效,其他迭代器不受任何影响。下面来看看List迭代器的源码。
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef __list_iterator<T, T&, T*> iterator; // 支持Iterator_traits
typedef __list_iterator<T, const T&, const T*> const_iterator;
typedef __list_iterator<T, Ref, Ptr> self;
// 以下为支持Iterator_traits而定义的一些类型
typedef bidirectional_iterator_tag iterator_category; //List的迭代器类型为双向迭代器
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef __list_node<T>* link_type;
// 这个是迭代器实际管理的资源指针
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; }
// 成员调用操作符
pointer operator->() const { return &(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的迭代器实现了==,!=,++,–,取值和成员调用等操作,由于是存放在不连续的内存空间,所以并不支持vector那样的p+n的操作。
- List的数据结构
List的数据结构个List的节点数据结构是分开定义的,SGI的List不仅是一个双向链表,而且还是一个环状双向链表,所以它只需要一个指针,就能完整表现一个链表.
template <class T, class Alloc = alloc>
class list
{
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
// 这个提供STL标准的allocator接口
typedef simple_alloc<list_node, Alloc> list_node_allocator;
// 链表的头结点,并不存放数据
link_type node;
//....以下还有一堆List的操作函数
}
- List构造函数
List提供了一个空构造函数,如下:
list() { empty_initialize(); }
// 用于空链表的建立
void empty_initialize()
{
node = get_node();
node->next = node; // 前置节点指向自己
node->prev = node; // 后置节点指向自己
}
另外,List还提供了带参的构造函数,支持如下初始化操作:
List<int> myList(5,1); // 初始化5个1的链表,{1,1,1,1,1}
其构造函数源码如下:
// 带参构造函数
list(size_type n, const T& value) { fill_initialize(n, value); }
// 创建值为value共n个结点的链表
void fill_initialize(size_type n, const T& value)
{
empty_initialize(); // 先创建一个空链表
insert(begin(), n, value); // 插入n个值为value的节点
}
// 在指定位置插入n个值为x的节点
void insert(iterator pos, int n, const T& x)
{
insert(pos, (size_type)n, x);
}
// 在position前插入n个值为x的元素
template <class T, class Alloc>
void list<T, Alloc>::insert(iterator position, size_type n, const T& x)
{
for ( ; n > 0; --n)
insert(position, x);
}
// 好吧,到这里才是真正的插入操作
// 很简单的双向链表插入操作
iterator insert(iterator position, const T& x)
{
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}