《STL源码剖析》笔记-list

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/WizardtoH/article/details/82592360

上一篇:《STL源码剖析》笔记-vector

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

list的节点

template <class T>
struct __list_node
{
    typedef void* void_pointer;
    void_pointer next;                // 前后指针都是void*类型
    void_pointer prev;
    T data;
};

以上是list节点的结构体,可以看出list是一个双向链表。

list的迭代器

list和vector不同,不能使用原始指针作为迭代器的value_type,因为list不能保证元素是线性连续的。list的迭代器需要具备前移、后移的能力,因此是Bidirectional Iterators。list的插入操作不会造成迭代器失效,删除操作只导致被删除的迭代器失效。

template<class T, class Ref, class Ptr>
struct __list_iterator
{
    typedef __list_iterator<T, T&, T*>      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; }

    // 以下对迭代器取值(dereference),取的是节点的数据值
    reference operator*() const { return (*node).data; }

    // 以下是迭代器的成员存取运算子的标准做法
    pointer operator->() const { return &(operator*()); }

    // 前缀自加,对迭代器累加1,就是前进一个节点
    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;
    }
};

list的数据结构

SGI STL中的list不仅仅是个双向链表,还是一个环状双向链表,因此只需要一个指针就能完整表现整个链表。


template <class T, class Alloc = alloc>
class list
{
protected:
    typedef void* void_pointer;
    typedef __list_node<T> list_node;
    typedef simple_alloc<list_node, Alloc> list_node_allocator;
public:
    typedef T 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* link_type;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

public:
    typedef __list_iterator<T, T&, T*>             iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;

protected:
    link_type node;                   // 只需要一个指针
...
}

然后让node指向一个位于尾端的空白节点,那么node便能符合STL对于前闭后开的要求,成为end()迭代器。这样,list相关的一些函数就能轻易实现了:

// list迭代器的构造函数中有一个接受link_type的版本
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }
// end()迭代器指向自身说明链表中无元素
bool empty() const { return node->next == node; }

// size函数使用distance进行遍历的原因详见https://blog.csdn.net/russell_tao/article/details/8572000
size_type size() const
{
    size_type result = 0;
    distance(begin(), end(), result);
    return result;
}

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

这里写图片描述

list的构造与内存管理

list的构造函数有很多,此处只例句默认构造函数。内存管理使用专属的list_node_allocator,配置单位为一个节点。

// 默认allocator为alloc
template <class T, class Alloc = alloc>
class list
{
...
public:
    list() { empty_initialize(); }
protected: 
    // 专属空间配置器,配置单位为一个节点大小
    typedef simple_alloc<list_node, Alloc> list_node_allocator;

    // 建立空链表
    void empty_initialize()
    {
        node = get_node();
        node->next = node;
        node->prev = node;
    }

    // 配置一个节点,不进行构造
    link_type get_node() { return list_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();
        construct(&p->data, x);
        return p;
    }

    // 析构并释放节点
    void destroy_node(link_type p)
    {
        destroy(&p->data);
        put_node(p);
    }
...
}

push_back、push_front函数内部调用的是insert函数:

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

// 在position之前插入节点
iterator insert(iterator position, const T& x)
{
    link_type tmp = create_node(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提供的元素操作函数很多,下面只举例常用的几个。

// 在链表前端插入结点
void push_front(const T& x) { insert(begin(), x); }
// 在链表最后插入结点
void push_back(const T& x) { insert(end(), x); }

// 移除迭代器position所指节点
iterator erase(iterator position){
    link_type next_node = link_type(position.node->next);
    link_type prev_node = link_type(position.node->prev);
    prev_node->next = next_node;
    next_node->prev = prev_node;
    destroy_node(position.node);
    return iterator(next_node);
}

// 删除链表第一个结点
void pop_front() { erase(begin()); }
// 删除链表最后一个结点
void pop_back(){
    iterator tmp = end();
    erase(--tmp);
}

// 销毁所有结点, 将链表置空
template <class T, class Alloc>
void list<T, Alloc>::clear()
{
    link_type cur = (link_type)node->next;
    while (cur != node)
    {
        link_type tmp = cur;
        cur = (link_type)cur->next;
        destroy_node(tmp);
    }
    // 恢复node原始状态
    node->next = node;
    node->prev = node;
}

// 移除容器内所有的相邻的重复结点
template <class T, class Alloc>
void list<T, Alloc>::unique()
{
    iterator first = begin();
    iterator last = end();
    if (first == last) return;
    iterator next = first;
    while (++next != last)
    {
        if (*first == *next)
            erase(next);
        else
            first = next;
        next = first;
    }
}

list内部提供一个所谓的迁移动作(transfer):将某连续范围的元素迁移到某个特定位置之前。技术上很简单,节点间的指标移动而已。这个动作为其它的复杂动作如 splice, sort, merge 等奠定良好的基础。

// 将 [first,last) 内的所有元素搬移到 position 之前
void transfer(iterator position, iterator first, iterator last)  
{  
    if (position != last)   // 如果last == position, 则相当于链表不变化, 不进行操作  
    {  
        (*(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)
    }  
}

// 将 x 接合于 position 所指位置之前。x 必须不同于 *this。 
void splice(iterator position, list& x)  
{  
    if (!x.empty())  
        transfer(position, x.begin(), x.end());  
}  

//将 i 所指元素接合于 position 所指位置之前。position 和 i 可指向同一个 list。
void splice(iterator position, list&, iterator i)  
{  
    iterator j = i;  
    ++j;  
    if (position == i || position == j) return;  
    transfer(position, i, j);  
}  

// 将 [first,last) 内的所有元素接合于 position 所指位置之前。
// position 和[first,last)可指向同一个 list,
// 但 position 不能位于[first,last)之内。
void splice(iterator position, list&, iterator first, iterator last)  
{  
    if (first != last)  
        transfer(position, first, last);  
}  

// 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();  

  // 注意:前提是,两个lists都已经递增排序  
  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()  
{  
    // 以下判断,如果是空白串行,或仅有㆒个元素,就不做任何动作。
    // 使用 size() == 0 || size() == 1 来判断,虽然也可以,但是比较慢。
    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() member function,
// 因为 STL 算法 sort() 只接受 RamdonAccessIterator.
// 本函式采用 quick sort
template <class T, class Alloc>
void list<T, Alloc> :: sort(){
    // 判断链表是否为空或者只有一个元素,就不做任何动作。
    // 使用 size() == 0 || size() == 1 来判断,虽然也可以,但是比较慢。
    if(node->next == node || link_type(node->next)->next == node){
        return;
    }

    list<T, Alloc> carry;
    list<T, 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]);
}

猜你喜欢

转载自blog.csdn.net/WizardtoH/article/details/82592360