STL源码剖析之list

相较于vector的连续线性空间,list就显得复杂的多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。

因此,list对于空间的运用有绝对的精准,一点不浪费,且对于任何位置的元素插入或者删除,list永远是常数时间。

 

与之前接触的一样,list本身和list节点结构是分开设计的。

List节点结构如下:

template <class T>
struct __list_node {
  typedef void* void_pointer;
  void_pointer next;
  void_pointer prev;
  T data;
};

很清楚的看出是个双向链表,使得整个list的操作变得相对简单些

 

 

1、首先看一下list的迭代器设计

template<class T, class Ref, class Ptr>
struct __list_iterator {
  typedef __list_iterator<T, T&, T*>             iterator;
  typedef __list_iterator<T, const T&, const T*>const_iterator;
  typedef __list_iterator<T, Ref, Ptr>           self;
 
  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中的那个节点
 
//构造函数
  __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; }
//另一种对成员取值的操作
#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;
  }
};


 

 

2、List的数据结构

List的数据结构跟我们想象的差不多,在此基础上,只要在链表尾部后刻意增加一个空白节点,就可以让整个链表形成环形链表。

 

图中的node(end)节点,即为一个空白节点。

整个链表实现取头、取尾、判断为空都可以依据这个节点来做出判断,如下:

 

  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;
  }
 
//更方便的是,当我们要交换两个list的内容时,我们只需要:
  void swap(list<T, Alloc>& x) { __STD::swap(node,x.node); }
//交换这一个节点就可以了


 

要注意的是,list的插入操作规范标准是:

         插入在…之前

 


3、list的构造与内存管理

List的内存管理在已有空间配置器的基础上作简单的调用

//配置一个节点的空间
  link_type get_node() { returnlist_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);
  }
 
protected:
  void empty_initialize() {
    node = get_node();
    node->next = node;
    node->prev = node;
  }
 
//链表的构造函数之一
  list() { empty_initialize(); }



4、接着,看一下list中的几个简单算法:

Transfer:(这是几个算法的基础)

//算法目的在于将 [fast, last)内的元素移到 position 元素之前
void transfer(iteratorposition, iterator first, iterator last) {
    if (position != last) {
      (*(link_type((*last.node).prev))).next =position.node;
      (*(link_type((*first.node).prev))).next =last.node;
      (*(link_type((*position.node).prev))).next= first.node; 
      link_type tmp =link_type((*position.node).prev);
      (*position.node).prev =(*last.node).prev;
      (*last.node).prev = (*first.node).prev;
      (*first.node).prev = tmp;
    }
  }

 

Splice:(调用Transfer的简单操作)

//将链list中的元素全部移到position中,position所在list和x 必须是不同的
 void splice(iterator position, list& x) {
    if (!x.empty())
      transfer(position, x.begin(), x.end());
  }
//将某list中的元素i移到position前面
  void splice(iterator position, list&, iterator i) {
    iterator j = i;
    ++j;
    if (position == i || position == j)return;
    transfer(position, i, j);
  }
//将某list中的【first, last)移到 position 前面
  void splice(iterator position, list&, iterator first,iterator last) {
    if (first != last)
      transfer(position, first, last);
  }

 

以下提供merge()和reverse() 、sort()算法

Merge():

//这个算法不多说,就跟普通的归并一样
template <class T,class Alloc>
void list<T,Alloc>::merge(list<T, 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);
}

Reverse:

template <class T,class Alloc>
void list<T,Alloc>::reverse() {
//先判断是不是0个或1个节点
  if (node->next == node ||link_type(node->next)->next == node)return;
//主要思想是利用begin()和transfer()来实现
//比如先指向第二个元素,利用transfer移到begin()前面,原先指向第二个元素的迭代器指向第三个,重复直到最后
  iterator first = begin();
  ++first;
  while (first != end()) {
    iterator old = first;
    ++first;
    transfer(begin(), old, first);
  }
}   


 

 

Sort有点看不懂,先放着

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值