list概述
list是双向链表 ,相比于vector的连续线性空间,list就显得复杂许多,她的好处是每次插入或删除一个元素,就配置或释放一个元素空间 。list对空间的运用绝对的精准,一点儿也不浪费。而且,对任何位置的元素插入或元素移除,list永远是常数时间。
list的节点
list本身和list节点是不同的结构,需要分开设计。以下是STL list的节点结构:
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的元素删除操作,也只有”指向被删除元素“的那个迭代器失效,其他迭代器不受任何影响。
以下是list迭代器的设计:
template<class T,class Ref,class Ptr> struct _list_iterator{ typedef _list_iterator<T,T&,T*> iterator; typedef _list_iterator<T,T&,T*> iterator; 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节点 //constructor _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;} //以下是迭代器的成员存取运算子的标准做法 reference operator->() const {return &(operator*());} //对迭代器累加1 self& operator++(){ node=(link_type)((*node).next); return *this; } self operator++(int){ self tmp=*this; ++*this; return tmp; } //对迭代器累减1 self& operator--(){ node=(link_type)((*node).prev); return *this; } self operator--(int){ self tmp=*this; --*this; return tmp; } }
list的数据结构
SGI list不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,便可以完整表现整个表。
template<class T,class Alloc = alloc> //缺省使用alloc为配置器:w class list{ protected : typedef __list_node<T> list_node ; public : typedef list_node* link_type ; protected : link_type node ; //只要一个指针,便可以表示整个环状双向链表 ... };
如果让指针node指向可以置于尾端的一个空白节点,node便符合STL对于“前闭后开”区间的要求,成为last迭代
器。
list的元素操作
push_back
当使用push_back将新元素插入list尾端时,此函数内部调用insert():
void push_back(const T& x) {insert(end(),x);}
insert
insert()是一个重载函数,有多种形式,其中最简单的一种如下:
iterator insert(iterator position, const T& x){//在迭代器position所指位置插入一个节点,内容为x link_type tmp = create_node(x); tmp->next = position.node; tmp->prev = position.node->node; (link_type(position.node->prev))->next = tmp; return tmp; }
push_front()
将新元素插入于list头端,内部调用insert()函数
void push_front(const T&x){ insert(begin(),x); }
eraser()
iterator erase(iterator position){ link_type next_node=link_type(position.node->next); link_type prev_node=link_type(position.node->prev_nodext); prev_node->next=next_node; next_node->prev=prev_node; destroy_node(position.node); return iterator(next_node); }
pop_front()
移除头结点,内部调用erase()函数
void pop_front(){ erase(begin()); }
pop_back()
移除尾结点,内部调用erase()函数
void pop_back(){ iterator i=end(); erase(--i); }
transfer()
将某连续范围的元素迁移到某个特定位置之前。技术上讲很简单,节点直接的指针移动而已。这个操作为其他复杂操作如splice,sort,merge等奠定了良好的基础。
void transfer(iterator position, iterator first, iterator last) { if (position != last) { (*(link_type((*last.node).prev))).next = position.node; //(1) (*(link_type((*first.node).prev))).next = last.node; //(2) (*(link_type((*position.node).prev))).next = first.node;//(3) link_type tmp = link_type((*position.node).prev); //(4) (*position.node).prev = (*last.node).prev; //(5) (*last.node).prev = (*first.node).prev; //(6) (*first.node).prev = tmp; //(7) } }