C++源码剖析——forward_list

时间:2021-01-12 01:20:23

  前言:之前看过侯老师的《STL源码剖析》但是那已经是多年以前的,现在工作中有时候查问题和崩溃都需要了解实际工作中使用到的STL的实现。因此计划把STL的源码再过一遍。
  摘要:本文描述了llvm中libcxx的forward_list的实现。
  关键字forward_list
  其他:参考代码LLVM-libcxx
  注意:参考代码时llvm的实现,与gnu和msvc的实现都有区别。

  forward_list是STL中的单向链表序列容器,可以在任意位置快速插入(前提是直到期望插入的位置),不支持快速的随机访问。需要注意的是因为forward_list是单向链表也就意味着size相关的函数可能是O(1)的。
  forward_list的定义如下:

template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS forward_list : private __forward_list_base<_Tp, _Alloc>{

};

1 __forward_list_base

C++源码剖析——forward_list

  forward_list是基于__forward_list_base实现的,其中只有一个头结点。

template <class _Tp, class _Alloc>
class __forward_list_base{
protected:
    __compressed_pair<__begin_node, __node_allocator> __before_begin_;
};

1.1 节点

  链表中由两种节点,一种是__begin_node是链表的头结点,另一种是__node是链表内数据成员的节点,其存储链表中保存的数据。

    typedef __forward_list_node<value_type, void_pointer>            __node;
    typedef __begin_node_of<value_type, void_pointer>                __begin_node;

  二者的区别就是将索引和值域区分了开来。从下面的代码可以看出__begin_node就是一个指针的adapter,而__forward_list_node和我们常见的链表节点没什么区别。这样做的好处是在使用迭代器时只需要一个指针的空间就可以表示索引,而不用每个迭代器都携带一个没用的value域,浪费空间。

template <class _NodePtr>
struct __forward_begin_node{
    typedef _NodePtr pointer;
    typedef __rebind_pointer_t<_NodePtr, __forward_begin_node> __begin_node_pointer;
    pointer __next_;
    _LIBCPP_INLINE_VISIBILITY __forward_begin_node() : __next_(nullptr) {}
    __begin_node_pointer __next_as_begin() const {
        return static_cast<__begin_node_pointer>(__next_);
    }
};

template <class _Tp, class _VoidPtr>
using __begin_node_of = __forward_begin_node<__rebind_pointer_t<_VoidPtr, __forward_list_node<_Tp, _VoidPtr> > >;

template <class _Tp, class _VoidPtr>
struct _LIBCPP_STANDALONE_DEBUG __forward_list_node : public __begin_node_of<_Tp, _VoidPtr>{
    typedef _Tp value_type;
    value_type __value_;
};

1.2 __forward_list_base

  __forward_list_base就是存储了一个节点__before_begin_,简单封装了一些拷贝和move的函数,唯一有点儿代码的就是clear(),代码也比较简单就是用for-loop析构对象释放节点的内存。

template <class _Tp, class _Alloc>
void __forward_list_base<_Tp, _Alloc>::clear() _NOEXCEPT{
    __node_allocator& __a = __alloc();
    for (__node_pointer __p = __before_begin()->__next_; __p != nullptr;){
        __node_pointer __next = __p->__next_;
        __node_traits::destroy(__a, _VSTD::addressof(__p->__value_));
        __node_traits::deallocate(__a, __p, 1);
        __p = __next;
    }
    __before_begin()->__next_ = nullptr;
}

2 forward_list

2.1 迭代器

  在了解具体实现前明白迭代器是如何实现的可以帮助我们理解内存布局方式。forward_list迭代器的实现比较简单就是一个__forward_list_node的指针。

template <class _NodePtr>
class _LIBCPP_TEMPLATE_VIS __forward_list_iterator{
    //__forward_list_node *
    __iter_node_pointer __ptr_;
};

  因为是单项链表因此只支持一种方向的索引,实现也比较简单就是当前节点的next指针。

__forward_list_iterator& operator++(){
        __ptr_ = __traits::__as_iter_node(__ptr_->__next_);
        return *this;
    }

2.2 forward_list

  forward_list是继承自__forward_list_base

template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS forward_list : private __forward_list_base<_Tp, _Alloc>{};

  因为是单向链表因此只允许头插,实现也比较简单就是创建节点,然后将对应的指针指向对应的节点。

template <class _Tp, class _Alloc>
void forward_list<_Tp, _Alloc>::push_front(value_type&& __v){
    __node_allocator& __a = base::__alloc();
    typedef __allocator_destructor<__node_allocator> _Dp;
    unique_ptr<__node, _Dp> __h(__node_traits::allocate(__a, 1), _Dp(__a, 1));
    __node_traits::construct(__a, _VSTD::addressof(__h->__value_), _VSTD::move(__v));
    __h->__next_ = base::__before_begin()->__next_;
    base::__before_begin()->__next_ = __h.release();
}

template <class _Tp, class _Alloc>
void forward_list<_Tp, _Alloc>::pop_front()
{
    __node_allocator& __a = base::__alloc();
    __node_pointer __p = base::__before_begin()->__next_;
    base::__before_begin()->__next_ = __p->__next_;
    __node_traits::destroy(__a, _VSTD::addressof(__p->__value_));
    __node_traits::deallocate(__a, __p, 1);
}

template <class _Tp, class _Alloc>
typename forward_list<_Tp, _Alloc>::iterator
forward_list<_Tp, _Alloc>::insert_after(const_iterator __p, const value_type& __v){
    __begin_node_pointer const __r = __p.__get_begin();
    __node_allocator& __a = base::__alloc();
    typedef __allocator_destructor<__node_allocator> _Dp;
    unique_ptr<__node, _Dp> __h(__node_traits::allocate(__a, 1), _Dp(__a, 1));
    __node_traits::construct(__a, _VSTD::addressof(__h->__value_), __v);
    __h->__next_ = __r->__next_;
    __r->__next_ = __h.release();
    return iterator(__r->__next_);
}

  其他几个函数的实现都是改变节点之类的比较简单就不详细描述了。