List 就是链表,这个我们在很久之前就学习过了单向链表 ,双向链表 。之前对STL有过大概的剖析,但没涉及到链表,早几天使用到了STL中的,对其中的有些细节不明了,特意重新阅读了下源码,对STL中的List 加深一下理解。。。。论指针(地址)的重要性。
附带说一句,阅读源码,建议使用Source Insight,非常好的源码阅读工具,没有其二。
1、List 概述
list 和 vector 是两个最常用的容器(序列式容器)。二者最显著的区别自然就是vector是连续线性空间,list则是不连续线性空间,相比于vector(动态数组),它的好处是每次插入和删除一个元素,只需配置或释放一个元素空间,对于任何位置的元素插入或元素移除,list永远是常数时间。尺有所长,寸有所短,list 寻址某一元素也没有vector(常数时间)方便。那么什么时机下最适合使用哪一种容器呢?必须视元素的多寡,元素的构造复杂度,元素存取行为的特定而定。
2、list 的节点
侯捷先生剖析的是STL203版的源码,我们这里紧跟下时尚,来剖析最新的STL源码http://www.sgi.com/tech/stl/download.html
以下是STL list的节点结构:
1 2 3 4 5 6 7 8 struct _List_node_base { _List_node_base* _M_next; _List_node_base* _M_prev; }; template <class _Tp >struct _List_node : public _List_node_base { _Tp _M_data; };
显然这是一个双向链表,链表节点的数据部分单独的放在一个结构体中,然后通过继承来获得链表的先后指针。node_base中仅含指针,这赶脚有点类似于linux内核中的list。
\ 3、list 的迭代器**
因为链表节点在储存空间中不一定是连续存在的,自然也就不能像vector一样用普通指针作为迭代器。list迭代器必须有能力指向list的节点,并有能力正确的递增。递减。取值、成员存取等操作。这是作为任何一个容器的迭代器必须具备的。对于非连续空间的递增,递减等操作自然是针对该容器的属性来的,就list而言,则是指向上一个节点,下一个节点,以及获取指定节点。
STL list是一个双向链表,迭代器必须具备前移,后移的能力,所以list提供的是bidirectional iterator (双向一个个移动)。关于迭代器的分类,参见STL迭代器 。vector是随机迭代器类型(random access iterator),这也是符合vector 可随机存取的能力。
以下是list迭代器的设计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 struct _List_iterator_base { typedef size_t size_type; typedef ptrdiff_t difference_type; typedef bidirectional_iterator_tag iterator_category; _List_node_base* _M_node; _List_iterator_base(_List_node_base* __x) : _M_node(__x) {} _List_iterator_base() {} void _M_incr() { _M_node = _M_node->_M_next; } void _M_decr() { _M_node = _M_node->_M_prev; } bool operator ==(const _List_iterator_base& __x) const { return _M_node == __x._M_node; } bool operator !=(const _List_iterator_base& __x) const { return _M_node != __x._M_node; } }; template <class _Tp , class _Ref , class _Ptr >struct _List_iterator : public _List_iterator_base { typedef _List_iterator<_Tp, _Tp&, _Tp*> iterator; typedef _List_iterator<_Tp, const _Tp&, const _Tp*> const_iterator; typedef _List_iterator<_Tp, _Ref, _Ptr> _Self; typedef _Tp value_type; typedef _Ptr pointer; typedef _Ref reference; typedef _List_node<_Tp> _Node; _List_iterator(_Node* __x) : _List_iterator_base(__x) {} _List_iterator() {} _List_iterator(const iterator& __x) : _List_iterator_base(__x._M_node) {} reference operator *() const { return ((_Node*)_M_node)->_M_data; } #ifndef __SGI_STL_NO_ARROW_OPERATOR pointer operator ->() const { return &(operator *()); } #endif _Self& operator ++() { this ->_M_incr(); return *this ; } _Self operator ++(int ) { _Self __tmp = *this ; this ->_M_incr(); return __tmp; } _Self& operator --() { this ->_M_decr(); return *this ; } _Self operator --(int ) { _Self __tmp = *this ; this ->_M_decr(); return __tmp; } };
struct 也具备class 一样的特性哦,差别在于成员的默认属性不一样。
\ 4、list 的数据结构**
表示源码中list的数据结构有点大,这里只贴出重要的核心部分,省略了一部分语义重复或结构重复的部分,面向对象编程就是所有的成员及操作均封装在一个类中,私有成员也都必须用成员函数(友元)访问,应用时,一个实例化对象直接调用这些操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 template <class _Tp , class _Alloc = __STL_DEFAULT_ALLOCATOR (_Tp ) >class list : protected _List_base<_Tp, _Alloc> {protected : _Node* _M_create_node(const _Tp& __x) { _Node* __p = _M_get_node(); __STL_TRY{ _Construct(&__p->_M_data, __x); } __STL_UNWIND(_M_put_node(__p)); return __p; } _Node* _M_create_node() { _Node* __p = _M_get_node(); __STL_TRY{ _Construct(&__p->_M_data); } __STL_UNWIND(_M_put_node(__p)); return __p; } public : explicit list (const allocator_type& __a = allocator_type()) : _Base (__a) {} iterator begin () { return (_Node*)(_M_node->_M_next); } const_iterator begin () const { return (_Node*)(_M_node->_M_next); } iterator end () { return _M_node; } const_iterator end () const { return _M_node; } bool empty () const { return _M_node->_M_next == _M_node; } size_type size () const { size_type __result = 0 ; distance(begin(), end(), __result); return __result; } size_type max_size () const { return size_type(-1 ); } reference front () { return *begin(); } const_reference front () const { return *begin(); } reference back () { return *(--end()); } const_reference back () const { return *(--end()); } void swap (list <_Tp, _Alloc>& __x) { __STD::swap(_M_node, __x._M_node); } iterator insert (iterator __position, const _Tp& __x) { _Node* __tmp = _M_create_node(__x); __tmp->_M_next = __position._M_node; __tmp->_M_prev = __position._M_node->_M_prev; __position._M_node->_M_prev->_M_next = __tmp; __position._M_node->_M_prev = __tmp; return __tmp; } void push_front (const _Tp& __x) { insert(begin(), __x); } void push_front () { insert(begin()); } void push_back (const _Tp& __x) { insert(end(), __x); } void push_back () { insert(end()); } iterator erase (iterator __position) { _List_node_base* __next_node = __position._M_node->_M_next; _List_node_base* __prev_node = __position._M_node->_M_prev; _Node* __n = (_Node*)__position._M_node; __prev_node->_M_next = __next_node; __next_node->_M_prev = __prev_node; _Destroy(&__n->_M_data); _M_put_node(__n); return iterator((_Node*)__next_node); } void clear () { _Base::clear(); } void resize (size_type __new_size, const _Tp& __x) ; void resize (size_type __new_size) { this ->resize(__new_size, _Tp()); } void pop_front () { erase(begin()); } void pop_back () { iterator __tmp = end(); erase(--__tmp); } ~list () { } list <_Tp, _Alloc>& operator =(const list <_Tp, _Alloc>& __x); public : void assign (size_type __n, const _Tp& __val) { _M_fill_assign(__n, __val); } void _M_fill_assign(size_type __n, const _Tp& __val); protected : void transfer (iterator __position, iterator __first, iterator __last) { if (__position != __last) { __last._M_node->_M_prev->_M_next = __position._M_node; __first._M_node->_M_prev->_M_next = __last._M_node; __position._M_node->_M_prev->_M_next = __first._M_node; _List_node_base* __tmp = __position._M_node->_M_prev; __position._M_node->_M_prev = __last._M_node->_M_prev; __last._M_node->_M_prev = __first._M_node->_M_prev; __first._M_node->_M_prev = __tmp; } } public : void splice (iterator __position, list & __x) { if (!__x.empty()) this ->transfer(__position, __x.begin(), __x.end()); } void splice (iterator __position, list &, iterator __i) { iterator __j = __i; ++__j; if (__position == __i || __position == __j) return ; this ->transfer(__position, __i, __j); } void splice (iterator __position, list &, iterator __first, iterator __last) { if (__first != __last) this ->transfer(__position, __first, __last); } void remove (const _Tp& __value) ; void unique () ; void merge (list & __x) ; void reverse () ; void sort () ; };
5、list的构造与内存管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 template <class _Tp , class _Alloc >class _List_base { public : typedef _Alloc allocator_type; allocator_type get_allocator () const { return allocator_type(); } _List_base(const allocator_type&) { _M_node = _M_get_node(); _M_node->_M_next = _M_node; _M_node->_M_prev = _M_node; } ~_List_base() { clear(); _M_put_node(_M_node); } void clear () ; protected : typedef simple_alloc<_List_node<_Tp>, _Alloc> _Alloc_type; _List_node<_Tp>* _M_get_node() { return _Alloc_type::allocate(1 ); } void _M_put_node(_List_node<_Tp>* __p) { _Alloc_type::deallocate(__p, 1 ); } protected : _List_node<_Tp>* _M_node; };
对于上面的配置和释放节点空间:allocate和deallocate函数参见前面博文: 第一级空间配置器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <class _Tp , class _Alloc >void _List_base <_Tp, _Alloc>: :clear(){ _List_node<_Tp>* __cur = (_List_node<_Tp>*) _M_node->_M_next; while (__cur != _M_node) { _List_node<_Tp>* __tmp = __cur; __cur = (_List_node<_Tp>*) __cur->_M_next; _Destroy(&__tmp->_M_data); _M_put_node(__tmp); } _M_node->_M_next = _M_node; _M_node->_M_prev = _M_node; }
上面的_Destrory函数参见博文: 构造与析构
6、list的元素操作
看看list数据结构中外部实现的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 template <class _Tp , class _Alloc >void list <_Tp, _Alloc>: :resize(size_type __new_size, const _Tp& __x){ iterator __i = begin(); size_type __len = 0 ; for (; __i != end() && __len < __new_size; ++__i, ++__len) ; if (__len == __new_size) erase(__i, end()); else insert(end(), __new_size - __len, __x); } template <class _Tp , class _Alloc >void list <_Tp, _Alloc>: :remove(const _Tp& __value){ iterator __first = begin(); iterator __last = end(); while (__first != __last) { iterator __next = __first; ++__next; if (*__first == __value) erase(__first); __first = __next; } } template <class _Tp , class _Alloc >void list <_Tp, _Alloc>: :unique(){ iterator __first = begin(); iterator __last = end(); if (__first == __last) return ; iterator __next = __first; while (++__next != __last) { if (*__first == *__next) erase(__next); else __first = __next; __next = __first; } } template <class _Tp , class _Alloc >void list <_Tp, _Alloc>: :merge(list <_Tp, _Alloc>& __x){ iterator __first1 = begin(); iterator __last1 = end(); iterator __first2 = __x.begin(); iterator __last2 = __x.end(); while (__first1 != __last1 && __first2 != __last2) if (*__first2 < *__first1) { iterator __next = __first2; transfer(__first1, __first2, ++__next); __first2 = __next; } else ++__first1; if (__first2 != __last2) transfer(__last1, __first2, __last2); } inline void __List_base_reverse(_List_node_base* __p){ _List_node_base* __tmp = __p; do { __STD::swap(__tmp->_M_next, __tmp->_M_prev); __tmp = __tmp->_M_prev; } while (__tmp != __p); } template <class _Tp , class _Alloc >inline void list <_Tp, _Alloc>: :reverse(){ __List_base_reverse(this ->_M_node); }
\ 7、详解list::sort()**
list中的sort函数比较拗口,不易理解,这里单独列出来,剖析一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 template <class _Tp , class _Alloc >void list <_Tp, _Alloc>: :sort(){ if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) { list <_Tp, _Alloc> __carry; list <_Tp, _Alloc> __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 ]); } }
如果实在不理解STL中的某些算法的细节是怎么运作的,你就把该算法源码copy一份,修改下个别参数类型,直接并入你的测试代码中,自己单步运行,看看中间数据结果,就一目了然了。这不失为一个好的理解源码的方式。 那么上面这个排序的时间复杂度如何呢? 归并算法 的时间复杂度为O(NlogN)。上面这个排序的时间复杂度也是O(NlogN)。
最后面补充一下STL中的list成员函数及其相应功能,如果只是单纯的使用STL来加快开发速度,下面这个表格够用了,但熟悉内部原理总是好的,知己知彼。借用西方的的一句话:不要重复造轮子。已经有这么优秀的list了,(Linux内核中的list ,本人认为更精妙)。就直接拿来用吧,或者适当修改以满足自己的实际开发需求。