您的位置:首页 > 其它

STL源码阅读-list

2014-07-25 21:14 483 查看
相比于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个数据,这个数据应该算是很庞大的,所以算法能够保持有效。这个算法确实写的比较精妙。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: