STL之list源码剖析

来源:互联网 发布:越南人长相知乎 编辑:程序博客网 时间:2024/05/09 22:41

STL之list源码剖析

相较于vector,使用list的好处是每次插入或删除一个元素,就配置或释放一个元素的空间。

list的节点:

template<class T>

struct  __list_node{

         typedefvoid* void_pointer;

         void_pointerprev;   //型别为void*。其实可设为__list_node<T>*

         void_pointernext;

         Tdata;

};

显然这是一个双向链表。

list迭代器

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

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

list有一个重要性质:插入操作和结合操作都不会造成原有的list迭代器失效,甚至删除操作也只有”指向被删除元素”的那个迭代器失效,其他迭代器不受影响。

以下是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;

 

  typedefbidirectional_iterator_tag iterator_category;

  typedefT value_type;

  typedefPtr pointer;

  typedefRef reference;

  typedef__list_node<T>* link_type;

  typedefsize_t size_type;

  typedefptrdiff_t difference_type;

 

 link_type node;

 

 __list_iterator(link_type x) : node(x) {}

 __list_iterator() {}

 __list_iterator(const iterator& x) : node(x.node) {}

 

  booloperator==(const self& x) const { return node == x.node; }

  bool operator!=(constself& x) const { return node != x.node; }

 reference operator*() const { return (*node).data; }

 

#ifndef __SGI_STL_NO_ARROW_OPERATOR

  pointeroperator->() const { return &(operator*()); }

#endif /* __SGI_STL_NO_ARROW_OPERATOR */

 

 self& operator++() {

    node= (link_type)((*node).next);

   return *this;

  }

  selfoperator++(int) {

    selftmp = *this;

   ++*this;

   return tmp;

  }

 self& operator--() {

    node= (link_type)((*node).prev);

   return *this;

  }

  selfoperator--(int) {

    selftmp = *this;

   --*this;

   return tmp;

  }

};

list的数据结构

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

template<class T,class Alloc= alloc> //缺省使用alloc为配置器

classlist{

protected:

   typedef __list_node<T> list_node;

public:

   typedef list_node* link_type;

 

protected:

   link_typenode; //只要一个指针,便可表示整个环状双向链表

   ...

};

让指针node指向置于尾端的一个空白节点,node变能符合STL对于”前开后闭”区间的要求,成为last(或者说end())迭代器。

如下图所示:


于是,以下几个函数便可以轻松完成:

iterator begin(){ return (link_type)((*node).next); }

iterator end(){ returnnode; }

boolempty() const { returnnode->next == node; }

size_type size() const{

   size_typereslut = 0;

   distance(begin(),end(), result);

   return result;

}

//取头结点的内容(元素值)

reference front(){ return *begin(); }

//取尾节点的内容(元素值)

reference back(){ return *(--end()); }

list的构造与内存管理:

list缺省使用alloc作为空间配置器,并据此另外定义了一个list_node_allocator,为方便以节点大小为配置单位:

template <class T, class Alloc = alloc>

class list{

protected:

  typedef__list_node<T> list_node;

  typedefsimple_alloc<list_node, Alloc>list_node_allocator;

   …

};

以下四个函数用来配置、释放、构造、销毁一个节点:

protected:

 link_type get_node() { return list_node_allocator::allocate(); }

  voidput_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;

  }

  voiddestroy_node(link_type p) {

    destroy(&p->data);

   put_node(p);

  }

list提供许多constrors,其中一个是defaultconstructors,允许我们不指定任何参数做出一个空的list出来:

public:

  list(){ empty_initialize(); }

protected:

  voidempty_initialize() {

    node= get_node(); //配置一个节点空间,令node指向它

   node->next = node; //令node头尾都指向自己,不设元素值

   node->prev = node;

  }

push_back()函数的实现:

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

insert是一个重载函数,有多重形式,以下是最简单的一种:

//函数目的:在迭代器position所指位置插入一个节点,内容为x

iterator insert(iterator position, const T&x) {

link_type tmp =create_node(x);    //产生一个节点(设内容为x)

//调整双向指针,使tmp插入进去

tmp->next =position.node;

   tmp->prev = position.node->prev;

   (link_type(position.node->prev))->next = tmp;

   position.node->prev = tmp;

return tmp;

}

注意,插入节点完成后,新节点位于哨兵迭代器所指节点的前方。

list的元素操作

list的主要元素操作有push_front, push_back, erase, pop_front, pop_back, clear,remove, unique,splice,merge,reverse,sort。

这里主要分析下splice,merge,reverse,sort这四个操作。

list内部提供一个所谓的迁移操作(transfer):将某连续范围内的元素迁移到某个特定位置之前。下面是transfer的源代码:

protected:

  voidtransfer(iterator position, 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;

    }

  }

下图展现transfer的实现:


上述的transfer并非公开接口。list公开提供的是所谓的接合操作(splice):将某个连续范围的元素从一个list移动到另一个(或同一个)list的某个定点。下面是splice几个版本的实现:

public:

  voidsplice(iterator position, list& x) {

    if(!x.empty())

     transfer(position, x.begin(), x.end());

  }

  voidsplice(iterator position, list&, iterator i) {

   iterator j = i;

    ++j;

    if(position == i || position == j) return;

   transfer(position, i, j);

  }

  voidsplice(iterator position, list&, iterator first, iterator last) {

    if(first != last)

     transfer(position, first, last);

  }

以下是merge(),reverse(), sort()的源代码:

//merge()将x合并到*this身上。两个lists的内容都必须先经过排序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()将*this的内容你想重置template <class T, class Alloc>void list<T, Alloc>::reverse() {//以下判断,如果是空链表,或仅有一个元素,就不进行任何操作  if(node->next == node || link_type(node->next)->next == node) return; iterator first = begin(); ++first;  while(first != end()) {   iterator old = first;   ++first;   transfer(begin(), old, first);  }}   //list不能使用STL算法sort(),必须使用自己的sort() memberfunction//因为STL算法sort()只接受RamdonAccessIterator//本函数采用quick sorttemplate <class T, class Alloc>void list<T, Alloc>::sort() {//以下判断,如果是空链表,或仅有一个元素,就不进行任何操作  if(node->next == node || link_type(node->next)->next == node) return;//一些新的lists,作为中介数据存放区 list<T, Alloc> carry; list<T, Alloc> counter[64];  intfill = 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]);}


0 0
原创粉丝点击