STL源码阅读-list

相比于vector,list在内存使用上更加精细,并且list在任何位置插入数据,都是常量时间。

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个数据,这个数据应该算是很庞大的,所以算法能够保持有效。这个算法确实写的比较精妙。















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值