STL之list源码剖析

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 sort
template <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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值