STL源码阅读-list
2014-07-25 21:14
483 查看
相比于vector,list在内存使用上更加精细,并且list在任何位置插入数据,都是常量时间。
list的部分基本定义如下:
与vector不同,list里面的Tp* 是pointer,而不是iterator,list需要对pointer进行封装,才能符合iterator的定义。
list继承自_List_base,_List_base的定义如下:
list_base中采用的节点是_list_node,这个结构体的定义如下:
上面_List_base在进行内存空间分配的时候,采用的是simple_alloc,不过其实质是一样的,都是采用_Alloc作为分配选择的。
simple_alloc的详细定义如下:
可以看到simple_alloc基本上是对_Alloc的简单封装吧。
到这里基本上list的节点内存分配应该就差不多了。
看一下list的一些关键操作。
front和back
可以看到 front 和back是直接调用begin,以及end,而begin和end 返回的是iterator,这个地方没看错了,以为返回的是指针,很纳闷为什么--end()能这么干。。。。
iterator的定义如下:
list的插入、删除、合并、拆分,算是比较重要的。
insert函数:
一个重要的函数,transfer,将[first,last)之间的数据移动到position之前
由于list不能提供RandomAccessIterator,所以list不能使用STL的sort,必须使用自己的sort函数。list的sort采用quicksort排序算法。
这个算法看了一下,侯捷在《STL源码解析》这本书中说这个算法是quick sort,怎么看都感觉不对,然后就仔细比对了一下,查了一些资料,这个算法应该还是属于归并排序,以1 4 3 6 7 2 这样一个序列为例,其排序过程为:
第一轮过后: counter[0] = {1}, carry = {}, counter[1] = {}, fill = 1; list={4,3,6,7,2}
第二轮过后: counter[0] = {},carry = {}, counter[1] = {1,4},fill = 2; list={3,6,7,2}
第三轮过后 :counter[0] = {3},carry={},counter[1] = {1,4}.fill = 2; list={6,7,2}
第四轮过后:counter[0] = {0},carry={},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={7,2};
第五轮过后: counter[0] = {7};carry={},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={2};
第六轮过后:counter[0] = {};carry={2,7},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={};
然后就跳出了while循环,进入 下面代码
for (int __i = 1; __i < __fill; ++__i)
__counter[__i].merge(__counter[__i-1]);
下面代码一次从0-3合并所有的序列,这样最终就得到了123467。最后进行carry和list交换,就完成排序过程了。 按照这个计算,由于总共有64个队列,所以最多能够排序的队列长度为2^64-1个数据,这个数据应该算是很庞大的,所以算法能够保持有效。这个算法确实写的比较精妙。
list的部分基本定义如下:
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) > class list : protected _List_base<_Tp, _Alloc> { // requirements: __STL_CLASS_REQUIRES(_Tp, _Assignable); typedef _List_base<_Tp, _Alloc> _Base; protected: typedef void* _Void_pointer; public: typedef _Tp value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef _List_node<_Tp> _Node; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef typename _Base::allocator_type allocator_type; allocator_type get_allocator() const { return _Base::get_allocator(); } public: typedef _List_iterator<_Tp,_Tp&,_Tp*> iterator; typedef _List_iterator<_Tp,const _Tp&,const _Tp*> const_iterator; #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION typedef reverse_iterator<const_iterator> const_reverse_iterator; typedef reverse_iterator<iterator> reverse_iterator; #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */ typedef reverse_bidirectional_iterator<const_iterator,value_type, const_reference,difference_type> const_reverse_iterator; typedef reverse_bidirectional_iterator<iterator,value_type,reference, difference_type> reverse_iterator; #endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ protected: #ifdef __STL_HAS_NAMESPACES using _Base::_M_node; using _Base::_M_put_node; using _Base::_M_get_node; #endif /* __STL_HAS_NAMESPACES */ 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; } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } 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()); }从list的定义里面可以看到,list采用的是stl的alloc作为内存空间的分配,因此在进行内存分配的时候,实际上是会略多于需要的内存空间的,存在一定的空间浪费,哈哈,这个确实有点吹毛求疵了。
与vector不同,list里面的Tp* 是pointer,而不是iterator,list需要对pointer进行封装,才能符合iterator的定义。
list继承自_List_base,_List_base的定义如下:
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; }; #endif /* __STL_USE_STD_ALLOCATORS */ 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) {//构成了环,所以不是__cur!=NULL _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; }_list_base里面是一些基本的操作,包括初始化之类的,clear函数中由于list的节点构成了一个环,因此在判断是否是到了链表的结尾。
list_base中采用的节点是_list_node,这个结构体的定义如下:
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; };可以看出来,list_node节点是一个双向链表,在看到这个定义之前,我在想STL怎么将这个list_node_base与tp_M_data帮顶起来,也就是说如果知道list_node_base的地址,怎么获得_Tp_M_data的地址,在linux 内核里面,有一种很精妙的方式,就是判断_Tp_M_data相对于0的位置,然后利用list_node_base加上这个差值,就可以得到需要的_Tp_M_data的位置。 这个里面采用继承的方式,也能够得到同样的效果。
上面_List_base在进行内存空间分配的时候,采用的是simple_alloc,不过其实质是一样的,都是采用_Alloc作为分配选择的。
typedef simple_alloc<_List_node<_Tp>, _Alloc> _Alloc_type;//采用simple_alloc作为内存分配的方式 _List_node<_Tp>* _M_get_node() { return _Alloc_type::allocate(1); }
simple_alloc的详细定义如下:
template<class _Tp, class _Alloc> class simple_alloc { public: static _Tp* allocate(size_t __n) { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); } static _Tp* allocate(void) { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); } static void deallocate(_Tp* __p, size_t __n) { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); } static void deallocate(_Tp* __p) { _Alloc::deallocate(__p, sizeof (_Tp)); } };
可以看到simple_alloc基本上是对_Alloc的简单封装吧。
到这里基本上list的节点内存分配应该就差不多了。
看一下list的一些关键操作。
front和back
reference front() { return *begin(); } const_reference front() const { return *begin(); } reference back() { return *(--end()); } const_reference back() const { return *(--end()); }
可以看到 front 和back是直接调用begin,以及end,而begin和end 返回的是iterator,这个地方没看错了,以为返回的是指针,很纳闷为什么--end()能这么干。。。。
iterator的定义如下:
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 /* __SGI_STL_NO_ARROW_OPERATOR */ _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; } };就是将pointer的操作进行了封装,从而符合iterator的要求。
list的插入、删除、合并、拆分,算是比较重要的。
insert函数:
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; }典型的链表插入,分配一个存储空间,然后调整pre和next指针就可以了。
一个重要的函数,transfer,将[first,last)之间的数据移动到position之前
void transfer(iterator __position, iterator __first , iterator __last) { if (__position != __last) { // Remove [first, last) from its old position. __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; // Splice [first, last) into its new position. _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; } }这个就是移动指针。后面在很多地方有用到。
由于list不能提供RandomAccessIterator,所以list不能使用STL的sort,必须使用自己的sort函数。list的sort采用quicksort排序算法。
template <class _Tp, class _Alloc> void list<_Tp, _Alloc>::sort() { // Do nothing if the list has length 0 or 1. 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源码解析》这本书中说这个算法是quick sort,怎么看都感觉不对,然后就仔细比对了一下,查了一些资料,这个算法应该还是属于归并排序,以1 4 3 6 7 2 这样一个序列为例,其排序过程为:
第一轮过后: counter[0] = {1}, carry = {}, counter[1] = {}, fill = 1; list={4,3,6,7,2}
第二轮过后: counter[0] = {},carry = {}, counter[1] = {1,4},fill = 2; list={3,6,7,2}
第三轮过后 :counter[0] = {3},carry={},counter[1] = {1,4}.fill = 2; list={6,7,2}
第四轮过后:counter[0] = {0},carry={},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={7,2};
第五轮过后: counter[0] = {7};carry={},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={2};
第六轮过后:counter[0] = {};carry={2,7},counter[1]= {},counter[2] ={1,3,4,6},fill = 3,list={};
然后就跳出了while循环,进入 下面代码
for (int __i = 1; __i < __fill; ++__i)
__counter[__i].merge(__counter[__i-1]);
下面代码一次从0-3合并所有的序列,这样最终就得到了123467。最后进行carry和list交换,就完成排序过程了。 按照这个计算,由于总共有64个队列,所以最多能够排序的队列长度为2^64-1个数据,这个数据应该算是很庞大的,所以算法能够保持有效。这个算法确实写的比较精妙。
相关文章推荐
- SGI_STL slist 源码阅读笔记
- 迭代器的型别 《STL 源码剖析》阅读笔记
- [Chrome源码阅读] 理解ObserverList类的实现技巧
- STL源码阅读-deque
- Loki源码阅读之TypeList(未完)
- 《STL源码剖析》---stl_slist.h阅读笔记
- SGI STL 源码阅读和分析 (1)
- STL源码—list transfer merge reverse
- STL源码阅读-iterator
- STL源码阅读-allocator
- STL源码分析--list
- STL源码阅读 -- basic_string
- STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort
- STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort
- STL源码阅读-vector
- STL源码剖析学习五:list
- STL源码分析----神奇的 list 的 sort 算法实现
- JDK源码阅读之List和AbstractSequentialList
- STL源码阅读-hashtable
- STL源码—list