stl源码剖析序列式容器之list

相比较于上一篇文章说到的vector,list就复杂很多,但是它每次插入或者删除一个元素,就配置或者释放一个元素空间。因此,list对于空间的运用有绝对的精准,对于任何位置的插入或删除,list都是常数时间。这两个是最常用的容器,一般是由元素的多少,元素的构造复杂度,元素的存取行为特性等决定使用哪一种容器的。
对于list来说,list本身和list的节点是两种不同的结构,需要对它进行分开设计,其中list的节点结构如下所示:

    

template<calss T>
    struct _list_node{
        typedef void* void_pointer;
        void_pointer prev;//类型为void*,其实可以弄成_lsit_node<T>*.
        void_pointer next;
        T data;
    }



结构如图:

一个很明显的双向链表,容器中一个很重要的东西就是迭代器,并且很明显,不能再用普通指针作为迭代器了,因为list并不是在存储空间中连续存在的。list的迭代器要成功实现递增、递减、取值、成员存取等操作。所以list应该提供的是bidirectional iterators(双向迭代器)。list和vector不同的一个重要性质,无论是结合操作(splice)或是插入操作(insert)都不会造成原有的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;
        // 拥有一个指向对应结点的指针,一个普通指针,指向list节点。
        link_type node;
        // 构造函数
        __list_iterator() {}
        __list_iterator(link_type x) : node(x) {}
        __list_iterator(const iterator& x) : node(x.node) {}    
        // 在STL算法中需要迭代器提供支持
        bool operator==(const self& x) const { return node == x.node; }
        bool operator!=(const self& x) const { return node != x.node; }
        // 重载了iterator必须的操作符,对迭代器取值,取的是节点的数据值
        reference operator*() const { return (*node).data; }
        //对迭代器的成员存取(member access)运算子的标准做法
        pointer operator->() const { return &(operator*()); }
        // 前缀自加,对迭代器累加1,就是前进一个节点,为了区分前后,用++()表示前自增,用++(int)后自增,传入一个0;
        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的数据结构,并且list还是一个环状的双向链表,所以只需要一个指针,便可以通过遍历的方式完整的表现出整个数组。

    

template <class T, class Alloc = alloc>  
    class list  
    {  
    protected:  
     typedef __list_node<T> list_node;
    public:
    typedef list_node* link_type;
    protected:
    link_type node;
    ......
    }


为了满足vector中的很多函数,所以将node刻意的指向尾端的一个空白节点,这样就实现了前闭后开的区间要求。这样下面的这些函数都能很好的完成。

下面这些函数里面不仅有list的内存管理和构造,还有它的元素操作,

    

iterator begin(){return (link_type)((*node).next);}
    iterator end(){return node;}
    bool empty()const{return node->next==node;}
    size_type size()const{
    size_type result=0;
    distance(begin(),end(),result);//全局函数,用于计算两个迭代器之间的距离,针对不同迭代器计算方式不同。
    return result;
    }
    //取头尾节点的值
    reference front(){return *begin();}
    reference back(){return *(--end());}

    // 默认allocator为alloc, 其具体使用版本请参照<stl_alloc.h>  
    template <class T, class Alloc = alloc>  
    class list  
    {  
    protected:  
        typedef void* void_pointer;  
        typedef __list_node<T> list_node;  
      
        // 专属之空间配置器,每次配置一个节点大小,list_node_allocator(n)表示配置n个节点空间 
        typedef simple_alloc<list_node, Alloc> list_node_allocator;  
      
    public:  
        typedef T value_type;  
        typedef value_type* pointer;  
        typedef value_type& reference;  
        typedef list_node* link_type;  
        typedef size_t size_type;  
        typedef ptrdiff_t difference_type;  
      
        typedef __list_iterator<T, T&, T*>             iterator;  
      
    protected:  
        link_type node ;     // 只要一个指针,便可表示整个环状双向链表  
        // 分配一个新结点, 注意这里并不进行构造,  
        // 构造交给全局的construct, 见<stl_stl_uninitialized.h> ,配置一个节点内存空间并传回。 
        link_type get_node() { return list_node_allocator::allocate(); }  
      
        // 释放指定结点, 不进行析构, 析构交给全局的destroy,这里直接进行内存释放了。  
        void put_node(link_type p) { list_node_allocator::deallocate(p); }  
      
        // 产生(配置并构造)一个节点, 首先分配内存, 然后进行构造  
        // 注: commit or rollback 提交或者回滚,就是成功或者释放所有申请的。  
        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);  
        }  
      
    protected:  
        // 用于空链表的建立  
        void empty_initialize()  
        {  
            node = get_node();   // 配置一个节点空间,令node指向它,显然为设定初值。  
            node->next = node;   // 令node头尾都指向自己,不设元素值  
            node->prev = node;  
        }  
      // 创建值为value共n个结点的链表  
      // 注: commit or rollback  
        void fill_initialize(size_type n, const T& value)  
        {  
            empty_initialize();  
            __STL_TRY  
            {  
                // 此处插入操作时间复杂度O(1)  
                insert(begin(), n, value);  
            }  
            __STL_UNWIND(clear(); put_node(node));  
        }  
          
      
    public:  
        list() { empty_initialize(); }//产生一个空链表,list包含很多constructors,这是个default constructor(默认构造函数)。  
        iterator begin() { return (link_type)((*node).next); }  
      
        // 链表成环, 当指所以头节点也就是end  
        iterator end() { return node; }  
      
        // 头结点指向自身说明链表中无元素  
        bool empty() const { return node->next == node; }  
      
        // 使用全局函数distance()进行计算, 时间复杂度O(n)  
        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(); }  
        reference back() { return *(--end()); }  
      
          
        // 在指定位置插入元素  
          
        //       insert(iterator position, const T& x)  
        //                       ↓  
        //                 create_node(x)  
        //                 p = get_node();-------->list_node_allocator::allocate();  
        //                 construct(&p->data, x);  
        //                       ↓  
        //            tmp->next = position.node;  
        //            tmp->prev = position.node->prev;  
        //            (link_type(position.node->prev))->next = tmp;  
        //            position.node->prev = tmp;  
          
        //首先配置一个节点,然后在尾端进行适当的指针操作,将新节点插入进去。
        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;//插入后返回的依然是插入点,插入是指的插入在..之前。  
        }  
      
      // 指定位置插入n个值为x的元素, 详细解析见实现部分  
      void insert(iterator pos, size_type n, const T& x);  
      void insert(iterator pos, int n, const T& x)  
      {  
          insert(pos, (size_type)n, x);  
      }  
      void insert(iterator pos, long n, const T& x)  
      {  
          insert(pos, (size_type)n, x);  
      }  
      
      // 在链表前端插入结点  
      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);  
      }  
      
      // 擦除一个区间的结点, 详细解析见实现部分  
      iterator erase(iterator first, iterator last);  
      
      void resize(size_type new_size, const T& x);  
      void resize(size_type new_size) { resize(new_size, T()); }  
      void clear();  
      
      // 删除链表第一个结点  
      void pop_front() { erase(begin()); }  
      // 删除链表最后一个结点  
      void pop_back()  
      {  
          iterator tmp = end();  
          erase(--tmp);  
      }  
      
      list(size_type n, const T& value) { fill_initialize(n, value); }  
      list(int n, const T& value) { fill_initialize(n, value); }  
      list(long n, const T& value) { fill_initialize(n, value); }  
      
      ~list()  
      {  
        // 释放所有结点  // 使用全局函数distance()进行计算, 时间复杂度O(n)  
      size_type size() const  
      {  
        size_type result = 0;  
        distance(begin(), end(), result);  
        return result;  
      }  
      clear();  
      // 释放头结点  
      put_node(node);  
      }  
      
      list<T, Alloc>& operator=(const list<T, Alloc>& x);  
      
    protected:  
      
          
        // 将[first, last)内的所有元素移动到position之前  
        // 如果last == position, 则相当于链表不变化, 不进行操作  
          
        // 初始状态  
        //                   first                             last  
        //                     ↓                                 ↓  
        //      --------   --------   --------     --------   --------   --------  
        //      | next |-->| next |-->| next |     | next |-->| next |-->| next |  
        //  ... --------   --------   -------- ... --------   --------   -------- ...  
        //      | prev |<--| prev |<--| prev |     | prev |<--| prev |<--| prev |  
        //      --------   --------   --------     --------   --------   --------  
        //  
        //                           position  
        //                               ↓  
        //      --------   --------   --------   --------   --------   --------  
        //      | next |-->| next |-->| next |-->| next |-->| next |-->| next |  
        //  ... --------   --------   --------   --------   --------   -------- ...  
        //      | prev |<--| prev |<--| prev |<--| prev |<--| prev |<--| prev |  
        //      --------   --------   --------   --------   --------   --------  
        //  
        // 操作完成后状态  
        //                           first  
        //                             |  
        //               --------------|--------------------------------------  
        //               | ------------|------------------------------------ |   last  
        //               | |           ↓                                   | |     ↓  
        //      -------- | |        --------   --------     --------       | |  --------   --------  
        //      | next |-- |  ----->| next |-->| next |     | next |-----  | -->| next |-->| next |  
        //  ... --------   |  |     --------   -------- ... --------    |  |    --------   -------- ...  
        //      | prev |<---  |  ---| prev |<--| prev |     | prev |<-- |  -----| prev |<--| prev |  
        //      --------      |  |  --------   --------     --------  | |       --------   --------  
        //                    |  |                                    | |  
        //                    |  ------                               | |  
        //                    ------- |  ------------------------------ |  
        //                          | |  |                              |  
        //                          | |  |  -----------------------------  
        //                          | |  |  |  
        //                          | |  |  |  position  
        //                          | |  |  |     ↓  
        //      --------   -------- | |  |  |  --------   --------   --------   --------  
        //      | next |-->| next |-- |  |  -->| next |-->| next |-->| next |-->| next |  
        //  ... --------   --------   |  |     --------   --------   --------   -------- ...  
        //      | prev |<--| prev |<---  ------| prev |<--| prev |<--| prev |<--| prev |  
        //      --------   --------            --------   --------   --------   --------  
          
        void transfer(iterator position, iterator first, iterator last)  
        {  
            if (position != last)   // 如果last == position, 则相当于链表不变化, 不进行操作  
            {  
                (*(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;  
            }  
        }  
    //list内部提供一个迁移操作,这个操作将连续范围内的元素迁移到某个特定位置之前,这个操作为后续的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)
        }
    } 
    public:  
        // 将链表x移动到position所指位置之前,x必须不同与*this  
        void splice(iterator position, list& x)  
        {  
            if (!x.empty())  
                transfer(position, x.begin(), x.end());  
        }  
      
        // 将链表中i指向的内容移动到position之前,两者可以在同一个list中。  
        void splice(iterator position, list&, iterator i)  
        {  
            iterator j = i;  
            ++j;  
            if (position == i || position == j) return;  
            transfer(position, i, j);//移动的时候不会包括j  
        }  
      
        // 将[first, last}元素移动到position之前,依然可以指向同一个list  
        void splice(iterator position, list&, iterator first, iterator last)  
        {  
            if (first != last)  
                transfer(position, first, last);  
        }  
      
        void remove(const T& value);  
        void unique();  
        void merge(list& x);  
        void reverse();  
        void sort();  
      
    };  
    // 销毁所有结点, 将链表置空  
    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;  
    } 
    //将数值为value的所有元素移除
    template <class T, class Alloc>  
    void list<T, Alloc>::remove(const T& value)  
    {  
        iteration first=begin();
        iteration last=end();
        while(first!=last)
        {
            iterator next=first;
            ++next;
            if(*first==value)earse(first);
            first=next;
        }  
    } 
    // 移除容器内所有的相邻的重复结点,只有连续且相同,才会被移除到只剩下一个。  
    // 时间复杂度O(n)  
    // 用户自定义数据类型需要提供operator ==()重载  
    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;  
      }  
    } 
    // 假设当前容器和x都已序, 保证两容器合并后仍然有序,将x合并到*this上,  
    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);//最后结果也可以看出是归在list1里面的。  
    }  
    //将*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函数,因为stl的sort只接受randonaccessiterator,下面这个函数采用的是归并排序的思想。
    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用于做中介数据存放区;
        list<T,Alloc>carry;//仅仅作为一个中转的链表
        list<T,Alloc>counter[64];//链表数组,counter[i]会存储2^i个元素,并且是有序的。
        int fill=0;
        while(!empty())
        {
            carry.splice(carry.begin(),*this,begin());//取出首元素放在carry中。
            int i=0;
            while(i<fill&&!counter[i].empty())
            {
                counter[i].merge(carry);//将carry中的元素归并到counter[i]中去。
                carry.swap(counter[i++]);//交换carry与counter的值,这是为了可以接着归并。
            }
            carry.swap(counter[i]);//上述没有继续下去之后,再把carry中的元素交换到counter[i]中。
            if(i==fill)++fill;//此时如果i==fill,说明下一次需要用到i++的归并空间了,所以fill++。
        }
        for(int i=1;i<fill;++i)
            counter[i].merge(counter[i-1]);//将所有未归并的元素归并起来,并且结果放在最后一个链表中。
        swap(counter[fill-1]);//取出最后一个,重新交换会list中。
    }  
    //详细讲解过程可以看 https://blog.csdn.net/chenhanzhun/article/details/39337331



下面是一些上面可能提到的结构的图片:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值