STL容器——list
相较于vector
的连续线性空间,list
就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。因此,list
对于空间的运用有绝对的精准,一点也不浪费,而且,对于任何位置的元素插入或元素删除,list
永远是常数时间。
STL list节点结构
template<class T>
struct __list_node{
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
};
list迭代器
list
不再能够像vector
一样以普通指针作为迭代器,因为其节点不保证在储存空间中连续存在。
由于STL list
是一个双向链表,迭代器必须具有前移,后移的能力,所以list
提供的是bidirectional iterator
。
list
有一个重要性质:插入操作(Insert)和接合操作(splice)都不会造成原有的list
迭代器失效。这在vector
是不成立的,因为vector
的插入操作可能造成数据重新配置,导致原有的迭代器全部失效。甚至list
的元素删除操作(erase)也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。
//以下是list迭代器的设计
template<class T, class Ref, class Ptr>
struct __list_iterator{
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, Ref, Ptr> self;
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
link_type node; //迭代器内部的普通指针,指向list的节点
__list_iterator(link_type x) : node(x) {}
__list_iterator(){}
__list_iterator(const iterator& x) : node(x.node) {}
bool operator==(const self& x) const { return node == x.node; }
bool operator!=(const self& x) const { return node != x.node; }
reference operator*() const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
//对迭代器累加1,就是前进一个节点
self& operatror(){
node = (link_type)((*node).next);
return *this;
}
self operator++(int){
self temp = *this;
++*this;
return temp;
}
//对迭代器递减1,就是后退一个节点
self& operator--(){
node = (link_type)((*node).prev);
return *this;
}
self operator--(int){
self temp = *this;
--*this;
return temp;
}
};
list的数据结构
SGI list
不仅是一个双向链表,而且还是一个环状双向链表,所以它只需要一个指针,便可以完整表现整个链表。
template<class T, class Alloc = alloc>
class list{
protected:
typedef __list_node<T> list_node;
public:
typedef list_node* link_type;
protected:
link_type node; //只要一个指针,便可以表示整个环状双向链表
...
};
如果让指针node
指向可以置于尾端的一个空白节点,node
便能符合STL
对于“前闭后开”区间的要求,成为last
迭代器。
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }
bool empty() const { return node->next == node; }
size_type size() const {
size_type result = 0;
distance(begin(), end(), result);
return result;
}
//取头结点的内容(元素值)
reference front() { return *begin(); }
//取为节点的内容(元素值)
reference back() { return *(--end()); }
list的构造与内存管理,元素操作
list
缺省使用alloc
作为空间配置器,并根据此定义了一个list_node_allocator
,为的是更方便的以节点大小为配置单位。
template<class T, class Alloc = alloc>
class list{
protected:
typedef __list_node<T> list_node;
//专属之空间配置器,每次配置一个节点大小
typedef simple_alloc<list_node, Alloc> list_node_allocator;
//配置一个节点并传回
link_type get_node() { return list_node_allocator::allocate(); }
//释放一个节点
void put_node(link_type p){ list_node_allocator::deallocate(p); }
//产生(配置并构造)一个节点,带有元素值
link_type create_node(const T& x){
link_type p = get_node();
construct(&p->data, x); //全局函数
return p;
}
//销毁(析构并释放)一个节点
void destory_node(link_type p){
destory(&p->data);
put_node(p);
}
public:
list() { empty_initialize(); } //产生一个空链表
protected:
void empty_initialize(){
node = get_node(); //配置一个节点空间
node->next = node;
node->prev = node;
}
//将元素插入尾端
void push_back(const T& x) { insert(end(), x); }
//在迭代器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;
}
//插入一个节点,作为头节点
void push_front(const T& x) { insert(begin(), x); }
//插入一个节点,作为尾节点
void push_back(const T& x) { insert(end(), x); }
//移除迭代器position所指节点
iterator erase(iterator position){
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destory_node(position.node);
return iterator(next_node);
}
//移除头节点
void pop_front() { erase(begin()); }
//移除尾节点
void pop_back(){
iterator tmp = end();
erase(--tmp);
}
//清除所有节点
void clear(){
link_type cur = (link_type)node->next;
while(cur != node) //遍历每一个节点
{
link_type tmp = cur;
cur = (link_type)cur->next;
destory_node(tmp);
}
//恢复node原始状态
node->next = node;
node->prev = node;
}
//将数值为value的所有元素移除
void remove(const T& value){
iterator first = begin();
iterator last = end();
while(first != last){
iterator next = first;
++next;
if(*first == value) erase(first);
first = next;
}
}
};
于是,list_node_allocator(n)
表示配置了n个节点空间。
注意,插入完成后,新节点将位于哨兵迭代器(标示出插入点)所指节点的前方,这是STL
对于插入操作的标准规范。由于list
不像vector
那样有可能在空间不足时做重新配置,数据移动的操作,所以插入前的所有迭代器在插入后都仍然有效。