【STL源码剖析】第四章 序列式容器 之 list底层实现

list

list概述

list是双向链表 ,相比于vector的连续线性空间,list就显得复杂许多,她的好处是每次插入或删除一个元素,就配置或释放一个元素空间 。list对空间的运用绝对的精准,一点儿也不浪费。而且,对任何位置的元素插入或元素移除,list永远是常数时间。

list的节点

list本身和list节点是不同的结构,需要分开设计。以下是STL list的节点结构:

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

显然是一个双向链表。

list的迭代器

list不能够像vector一样以普通指针作为迭代器,因为其节点不能保证在存储空间中连续存在。list迭代器必须有能力指向list的节点,并有能力进行递增、递减、取值、成员存取等操作。

由于STL list是一个双向链表,迭代器必须具备前移、后移的能力,所以list提供的是Bidirectional Iterators 。

list有一个重要性质:插入操作(insert)和接合操作(splice)都不会造成list迭代器失效。这在vector是不成立的,因为vector的插入操作可能造成记忆体重新配置,导致原有迭代器全部失效。甚至list的元素删除操作,也只有”指向被删除元素“的那个迭代器失效,其他迭代器不受任何影响。

以下是list迭代器的设计:

  
  template<class T,class Ref,class Ptr>
  struct _list_iterator{
      typedef _list_iterator<T,T&,T*> iterator;
      typedef _list_iterator<T,T&,T*> iterator;
  
      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节点
      
      //constructor
      _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;}
      //以下是迭代器的成员存取运算子的标准做法
      reference operator->() const {return &(operator*());}
      
      //对迭代器累加1
      self& operator++(){
          node=(link_type)((*node).next);
          return *this;
      }
      self operator++(int){
          self tmp=*this;
          ++*this;
          return tmp;
      }
      //对迭代器累减1
      self& operator--(){
          node=(link_type)((*node).prev);
          return *this;
      }
      self operator--(int){
          self tmp=*this;
          --*this;
          return tmp;
      }
  }
list的数据结构

SGI list不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,便可以完整表现整个表。

  
  template<class T,class Alloc = alloc> //缺省使用alloc为配置器:w  
  class list{  
  protected :  
      typedef __list_node<T> list_node ;  
  public  :  
      typedef list_node* link_type ;  
  protected :  
      link_type node ; //只要一个指针,便可以表示整个环状双向链表  
      ...
  };

如果让指针node指向可以置于尾端的一个空白节点,node便符合STL对于“前闭后开”区间的要求,成为last迭代

器。

list的元素操作

push_back

当使用push_back将新元素插入list尾端时,此函数内部调用insert():

  
  void push_back(const T& x) {insert(end(),x);}

insert

insert()是一个重载函数,有多种形式,其中最简单的一种如下:

  
  iterator insert(iterator position, const T& x){//在迭代器position所指位置插入一个节点,内容为x  
      link_type tmp = create_node(x);  
      tmp->next = position.node;  
      tmp->prev = position.node->node;  
      (link_type(position.node->prev))->next = tmp;  
      return tmp;  
  }

push_front()

将新元素插入于list头端,内部调用insert()函数

  
  void push_front(const T&x){  
  insert(begin(),x);  
  }

eraser()

  iterator erase(iterator position){  
      link_type next_node=link_type(position.node->next);  
      link_type prev_node=link_type(position.node->prev_nodext);  
      prev_node->next=next_node;  
      next_node->prev=prev_node;  
      destroy_node(position.node);  
      return iterator(next_node);  
  } 

pop_front()

移除头结点,内部调用erase()函数

  void pop_front(){  
      erase(begin());  
  } 

pop_back()

移除尾结点,内部调用erase()函数

  void pop_back(){  
      iterator i=end();  
      erase(--i);  
  } 

transfer()

将某连续范围的元素迁移到某个特定位置之前。技术上讲很简单,节点直接的指针移动而已。这个操作为其他复杂操作如splice,sort,merge等奠定了良好的基础。

  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)  
      }  
    }  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值