STL容器——list

STL容器——list

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

STL list节点结构

template<class T>
struct __list_node{
	typedef void* void_pointer;
    void_pointer prev;
    void_pointer next;
    T data;
};

在这里插入图片描述

list迭代器

list不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在储存空间中连续存在。

由于STL list是一个双向链表,迭代器必须具有前移,后移的能力,所以list提供的是bidirectional iterator

list有一个重要性质:插入操作(Insert)和接合操作(splice)都不会造成原有的list迭代器失效。这在vector是不成立的,因为vector的插入操作可能造成数据重新配置,导致原有的迭代器全部失效。甚至list的元素删除操作(erase)也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。

//以下是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; }
    
    reference operator*() const { return (*node).data; }
    pointer operator->() const { return &(operator*()); }
    
    //对迭代器累加1,就是前进一个节点
    self& operatror(){
        node = (link_type)((*node).next);
        return *this;
    }
    self operator++(int){
        self temp = *this;
        ++*this;
        return temp;
    }

    //对迭代器递减1,就是后退一个节点
    self& operator--(){
        node = (link_type)((*node).prev);
        return *this;
    }
    self operator--(int){
        self temp = *this;
        --*this;
        return temp;
    }
};

在这里插入图片描述

list的数据结构

SGI 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;		//只要一个指针,便可以表示整个环状双向链表
...
};

如果让指针node指向可以置于尾端的一个空白节点node便能符合STL对于“前闭后开”区间的要求,成为last迭代器。

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

在这里插入图片描述

list的构造与内存管理,元素操作

list缺省使用alloc作为空间配置器,并根据此定义了一个list_node_allocator,为的是更方便的以节点大小为配置单位。

template<class T, class Alloc = alloc>
class list{
protected:
    typedef __list_node<T> list_node;
    //专属之空间配置器,每次配置一个节点大小
    typedef simple_alloc<list_node, Alloc> list_node_allocator;
    
    //配置一个节点并传回
    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 destory_node(link_type p){
        destory(&p->data);
        put_node(p);
    }
public:
    list() { empty_initialize(); }	//产生一个空链表
    
protected:
    void empty_initialize(){
        node = get_node();		//配置一个节点空间
        node->next = node;
        node->prev = node;
    }
    
    //将元素插入尾端
    void push_back(const T& x) { insert(end(), x); }
    //在迭代器position的位置上插入一个节点,内容为x
    iterator insert(iterator position, const T& x){
        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;
        return tmp;
    }
    
    //插入一个节点,作为头节点
    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;
        destory_node(position.node);
        return iterator(next_node);
    }
    
    //移除头节点
    void pop_front() { erase(begin()); }
    //移除尾节点
    void pop_back(){
        iterator tmp = end();
        erase(--tmp);
    }
    //清除所有节点
    void clear(){
		link_type cur = (link_type)node->next;
        while(cur != node)		//遍历每一个节点
        {
         	link_type tmp = cur;
            cur = (link_type)cur->next;
            destory_node(tmp);
        }
        //恢复node原始状态
        node->next = node;
        node->prev = node;
    }
    //将数值为value的所有元素移除
    void remove(const T& value){
        iterator first = begin();
        iterator last = end();
        while(first != last){
            iterator next = first;
            ++next;
            if(*first == value) erase(first);
            first = next;
        }
    }
};

于是,list_node_allocator(n)表示配置了n个节点空间。

注意,插入完成后,新节点将位于哨兵迭代器(标示出插入点)所指节点的前方,这是STL对于插入操作的标准规范。由于list不像vector那样有可能在空间不足时做重新配置,数据移动的操作,所以插入前的所有迭代器在插入后都仍然有效。
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值