相对于vector的连续线性空间,list是非线性非连续空间,虽然会复杂一些,但是每次插入或者删除一个元素,只是配置或释放一个元素空间,而且对任何位置的元素插入或移除,list都是常数操作时间。
【节点结构】
list本身和list的结点是不同结构,需分开设计。
结点结构:
template struct __list_node{ typedef void* void_pointer; void_pointer next; void_pointer prev; T data;};
其实就是一个双向链表:
【迭代器】
1.list结点不保证在存储空间中连续,所以list不能像vector一样,以普通指针作为迭代器。
2.list迭代器必须有能力指向list的结点,并有能力进行正确的递增、递减、取值、成员存取操作。
3.list是一个双向链表,迭代器必须具备前移、后移的能力,所以list提供Bidirectional Iterators。
4.list的插入操作和接合操作都不会造成原有的list迭代器失效。这在vector是不能力的,因为vector的插入操作可能造成内存重新配置而导致原有的迭代器全部失效。
templatestruct __list_iterator { //STL要求每个迭代器设计都要有 iterator和const_iterator, //这里的self声明便于下文使用。 typedef __list_iterator iterator; typedef __list_iterator const_iterator; typedef __list_iterator self; typedef bidirectional_iterator_tag iterator_category; typedef T value_type; typedef Ptr pointer; typedef Ref reference; typedef __list_node* 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; }//迭代器的成员存取运算子的标准做法。//即如果Class T是个类或者结构体时,->就是//指向这些类或者结构体的内部元素。#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; }};
数据结构
SGI list不仅是一个双向链表,而且还是一个环状双向链表,所以只需一个指针,便可完整表现整个链表。
相关代码:
template class list{ protected: typedef void* void_pointer; typedef __list_node list_node; public: typedef list_node* link_type; protected: link_type node; ...}
如果让指针node指向刻意置于尾端的一个空白结点,node便能符合STL对于“前闭后开”区间的要求,成为last迭代器,如图2:
如果node指向尾端,则以下几个函数实现就很简单:
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缺省使用alloc作为空间配置器,并据此另外定义了一个list_node_allocator,为的是更方便以结点大小为配置单位。
template class list {protected:typedef __list_node list_node;//专属之空间配置器,每次配置一个节点大小typedef simple_alloc list_node_allocator;...protected://配置一个节点并传回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();__STL_TRY {construct(&p->data, x);}__STL_UNWIND(put_node(p));return p;}//销毁(析构并释放)一个节点void destroy_node(link_type p) {destroy(&p->data);put_node(p);}}list提供了许多constructors,其中一个是default constructor,允许我们不指定任何参数做出一个空的list出来:public: list() { empty_initialize(); } protected: void empty_initialize() { node = get_node(); node->next = node; node->prev = node; }
【元素操作】
简单的元素操作这里就不说了,操作都和循环链表操作方法一样,这里重点说下list内部提供的一个所谓的迁移操作(transfer):将某连续范围的元素迁移到某个特定位置之前。技术上很简单,节点间的指针移动而已。这个操作为其它的复杂操作如splice,sort,merge等提供了良好的基础。
Transfer源码:
将[first,last)内的所有元素移动到position之前。
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) } }
上述都是指针的移动操作,通过图中的序号可以很容易看懂。transfer所接受的[first,last)区间和position可以在同一个list,也可以在不同的list。
最后在说下list内部使用的sort算法。list不能使用STL的sort算法,因为STL算法sort()只接受RamdonAccessIterator。
template void list::sort() { if (node->next == node || link_type(node->next)->next == node) return; list carry; list counter[64]; int fill = 0; while (!empty()) { carry.splice(carry.begin(), *this, begin()); int i = 0; while(i < fill && !counter[i].empty()) { counter[i].merge(carry); carry.swap(counter[i++]); } carry.swap(counter[i]); if (i == fill) ++fill; } for (int i = 1; i < fill; ++i) counter[i].merge(counter[i-1]); swap(counter[fill-1]);}
书中说是quick sort,通过算法的实现方式来看其实更像是非递归归并排序。网上也有好多对此进行纠正说明的。对此算法的解读说明我这里也就不列了,大家可参考此篇链接:list::sort()深度解析