STL—list

前面咱们分析了vector,这篇介绍STL中另外一个重要的容器listhtml

list的设计

list由三部分构成:list节点、list迭代器、list自己node

list节点git

list是一个双向链表,因此其list节点中有先后两个指针。以下:github

// list节点
template <typename T>
struct __list_node
{
        typedef void* void_pointer;
        void_pointer prev; // 指向前一个节点
        void_pointer next; // 指向下一个节点
        T data; // 节点的值
};

list迭代器函数

前面咱们说过vector是利用其内存分配类型成员给vector分配一大块内存,而其迭代器是原始指针,因此其迭代器的移动就是指针的移动,vector那样经过指针的移动就能获得下一个元素,不须要特别设计。而list是链表结构,链表中每一个节点的内存不连续,list的迭代器就是对外隐藏了从一个节点是如何移动到下一个节点的具体细节,使得外部只要将迭代器自增或自减就能获得相邻的节点。this

list迭代器只有一个指向链表节点的指针数据成员。以下:spa

        typedef __list_node<T>* link_type;

        link_type node;  // point to __list_node

下面是迭代器的前置自增和前置自减运算符的源码,能够看到是经过节点的前向和后向指针来完成从一个节点移动到另外一个节点:设计

        self& operator++() { node = (link_type)(*node).next; return *this;}
        self& operator--() { node = (link_type)(*node).prev; return *this;}

list指针

和vector同样,list也有个空间配置器的类型成员,经过该类型来为list的每一个节点分配内存,而且经过该类型成员将外部指定的节点数目转换为相应节点所需的内存因此list的内存模型是每一个链表节点分配一块单独的内存,而后将每一个节点链接起来。而vector的内存模型是分配一大块连续的内存。以下:code

          // 空间配置器
          typedef simple_alloc<list_node, alloc> list_node_allocator;

 

实际上,list不只是一个双向链表,并且仍是一个环状的双向链表。为了设计的方便,在list中放置一个node指针,该指针指向一个空白节点,该空白节点的下一个节点是链表中起始节点,而链表的尾节点的下一个节点为该空白节点。虽然list底层是一个环状双向链表,但经过这样设计后对外就表现出一个普通的双向链表,符合通常习惯。这样设计还有不少好处,好比快速获得链表的首尾节点。以下。

private:
        //指向空白节点
        link_type               node;
public:
        // 经过空白节点node完成
        iterator begin() const { return (link_type)(*node).next; }
        iterator end() const { return node;}
        bool empty() const { return node->next == node; }

 

下面咱们看list内部是如何构造一个链表的。以咱们对list的经常使用使用方法 list<int> lis(10)为例:

首先调用构造函数

        explicit list(size_type n)
        {
                empty_initialize();
                insert(begin(), n, T());
        }

该构造函数会先调用empty_initialize()为list分配一个空白节点,并设置前向后向指针

        void empty_initialize()
        {
                node = get_node();
                node->next = node;
                node->prev = node;
        }
        link_type get_node() { return list_node_allocator::allocate(1);}

而后构造函数会循环以插入相应个数的链表节点,每次插入时会分配一个节点大小的内存,而后对这块内存初始化,注意插入位置是在指定位置以前插入。因为list的内存模型和vector内存模型的区别,vector每次插入时因为可能会形成内存的从新配置,会形成原先全部的迭代器失效。而list的插入只是为新节点分配内存,并将其添加到链表中,对链表中其余节点的内存不会形成影响,因此list的插入则不会引发迭代器失效。以下。

template <typename T>
void
list<T>::insert(iterator position, size_type n, const T& x)
{
        for (; n > 0; --n)
                insert(position, x);
}

template
<typename T> void list<T>::insert(iterator position, const T& x)//posiiton以前插入 { link_type tmp = create_node(x); tmp->next = position.node; tmp->prev = position.node->prev; (link_type(position.node->prev))->next = tmp; position.node->prev = tmp; }
link_type create_node(
const T& x) { link_type p = get_node(); construct(&p->data, x); return p; }
link_type get_node() {
return list_node_allocator::allocate(1);}

 

(全文完)

附:
一款简易版STL的实现,项目地址: https://github.com/zinx2016/MiniSTL/tree/master/MiniSTL
相关文章
相关标签/搜索