C++ STL 容器设计实现 -- forward_list

4、forward_list (C++11)

forward_list 是 C++11 引入的单链表数据结构。

4.1、_Fwd_list_node

首先是 forward_list 链表结点。_Fwd_list_node 继承于 _Fwd_list_node_base,成员 _M_storage 保存用户数据,_M_valptr() 函数返回 _M_storage 的地址。

/// forward_list.h
  template<typename _Tp>
    struct _Fwd_list_node
    : public _Fwd_list_node_base
    {
      _Fwd_list_node() = default;

      __gnu_cxx::__aligned_buffer<_Tp> _M_storage;

      _Tp*
      _M_valptr() noexcept
      { return _M_storage._M_ptr(); }

      const _Tp*
      _M_valptr() const noexcept
      { return _M_storage._M_ptr(); }
    };

foward_list 链表结点主要操作在基类 _Fwd_list_node_base 中定义。_Fwd_list_node_base 只有一个指针成员 next,初始化为 nullptr。

/// forward_list.h
  struct _Fwd_list_node_base
  {
    _Fwd_list_node_base() = default;
    _Fwd_list_node_base(_Fwd_list_node_base&& __x) noexcept
      : _M_next(__x._M_next)
    { __x._M_next = nullptr; }

    _Fwd_list_node_base(const _Fwd_list_node_base&) = delete;
    _Fwd_list_node_base& operator=(const _Fwd_list_node_base&) = delete;

    _Fwd_list_node_base&
    operator=(_Fwd_list_node_base&& __x) noexcept
    {
      _M_next = __x._M_next;
      __x._M_next = nullptr;
      return *this;
    }

    _Fwd_list_node_base* _M_next = nullptr; // next 指针,初始化为空
// ...
  };

_Fwd_list_node_base 有两个重要的成员函数:_M_transfer_after() 和 _M_reverse_after() 函数。

_M_transfer_after() 函数用于将 (begin, end] 转移到 *this 后面。如下图,_M_transfer_after() 函数返回后,this->next 指向值为 2 的结点。

/// forward_list.h
    _Fwd_list_node_base*
    _M_transfer_after(_Fwd_list_node_base* __begin,
              _Fwd_list_node_base* __end) noexcept
    {
      _Fwd_list_node_base* __keep = __begin->_M_next;
      if (__end)
    {
      __begin->_M_next = __end->_M_next;
      __end->_M_next = _M_next;
    }
      else
    __begin->_M_next = nullptr;
      _M_next = __keep;
      return __end;
    }

_M_reverse_after() 用于将链表反转。

4.2、_Fwd_list_iterator

forward_list 的迭代器区分 const 和 非 const 类型。两者保存的都是 \Fwd_list_node_base 类型的指针,指向对应的 node。当向 forward_list 插入和删除元素后,其他迭代器不会失效。

值得注意的是,forward_list 的迭代器数据成员 _M_node 是 public 成员,可以直接访问。

/// forward_list.h
  template<typename _Tp>
    struct _Fwd_list_iterator
    {
      typedef _Fwd_list_iterator<_Tp>		_Self;
      typedef _Fwd_list_node<_Tp>		_Node;

      typedef _Tp				value_type;
      typedef _Tp*				pointer;
      typedef _Tp&				reference;
      typedef ptrdiff_t				difference_type;
      typedef std::forward_iterator_tag		iterator_category;
// ...
      _Fwd_list_node_base* _M_node; // public
    };

  template<typename _Tp>
    struct _Fwd_list_const_iterator
    {
      typedef _Fwd_list_const_iterator<_Tp>	_Self;
      typedef const _Fwd_list_node<_Tp>		_Node;
      typedef _Fwd_list_iterator<_Tp>		iterator;

      typedef _Tp				value_type;
      typedef const _Tp*			pointer;
      typedef const _Tp&			reference;
      typedef ptrdiff_t				difference_type;
      typedef std::forward_iterator_tag		iterator_category;
// ....
      const _Fwd_list_node_base* _M_node;
    };

4.3、_Fwd_list_base

_Fwd_list_base 是 forward_list 的基类,定义了单链表 forward_list 头结点。

/// forward_list.h
  template<typename _Tp, typename _Alloc>
    struct _Fwd_list_base
    {
    protected:
      typedef __alloc_rebind<_Alloc, _Fwd_list_node<_Tp>> _Node_alloc_type;
      typedef __gnu_cxx::__alloc_traits<_Node_alloc_type> _Node_alloc_traits;

      struct _Fwd_list_impl
      : public _Node_alloc_type
      {
    _Fwd_list_node_base _M_head; // 头结点
// ...
      };

      _Fwd_list_impl _M_impl;

    public:
      typedef _Fwd_list_iterator<_Tp>		iterator;
      typedef _Fwd_list_const_iterator<_Tp>	const_iterator;
      typedef _Fwd_list_node<_Tp>		_Node;
// ...
    };

_M_get_node() 和 _M_put_node() 函数分别用于申请和释放一个结点,而 _M_create_node() 用于构造一个结点。注意到,_M_put_node() 并不负责析构一个结点,某个结点的析构显示调用 destroy() 执行,在 _M_erase_after() 函数中可以看到。

/// forward_list.h
      _Node*
      _M_get_node()
      {
    auto __ptr = _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
    return std::__to_address(__ptr);
      }

      template<typename... _Args>
    _Node*
    _M_create_node(_Args&&... __args)
    {
      _Node* __node = this->_M_get_node();
      __try
        {
          ::new ((void*)__node) _Node; // 构造指针
          _Node_alloc_traits::construct(_M_get_Node_allocator(),
                        __node->_M_valptr(),
                        std::forward<_Args>(__args)...); // 构造数据
        }
      __catch(...)
        {
          this->_M_put_node(__node);
          __throw_exception_again;
        }
      return __node;
    }

      void
      _M_put_node(_Node* __p)
      {
    typedef typename _Node_alloc_traits::pointer _Ptr;
    auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__p);
    _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ptr, 1);
      }

_M_insert_after() 函数负责在 pos 后插入一个元素。

/// forward_list.tcc
  template<typename _Tp, typename _Alloc>
    template<typename... _Args>
      _Fwd_list_node_base*
      _Fwd_list_base<_Tp, _Alloc>::
      _M_insert_after(const_iterator __pos, _Args&&... __args)
      {
    _Fwd_list_node_base* __to
      = const_cast<_Fwd_list_node_base*>(__pos._M_node);
    _Node* __thing = _M_create_node(std::forward<_Args>(__args)...);
    __thing->_M_next = __to->_M_next;
    __to->_M_next = __thing;
    return __to->_M_next;
      }

_M_erase_after() 函数负责将 pos 后的元素删除。

/// forward_list.tcc
  template<typename _Tp, typename _Alloc>
    _Fwd_list_node_base*
    _Fwd_list_base<_Tp, _Alloc>::
    _M_erase_after(_Fwd_list_node_base* __pos)
    {
      _Node* __curr = static_cast<_Node*>(__pos->_M_next);
      __pos->_M_next = __curr->_M_next;
      _Node_alloc_traits::destroy(_M_get_Node_allocator(),
                  __curr->_M_valptr()); // 析构数据
      __curr->~_Node();         // 析构结点
      _M_put_node(__curr);      // 释放结点内存
      return __pos->_M_next;
    }

  template<typename _Tp, typename _Alloc>
    _Fwd_list_node_base*
    _Fwd_list_base<_Tp, _Alloc>::
    _M_erase_after(_Fwd_list_node_base* __pos,
           _Fwd_list_node_base* __last)
    {
      _Node* __curr = static_cast<_Node*>(__pos->_M_next);
      while (__curr != __last)
    {
      _Node* __temp = __curr;
      __curr = static_cast<_Node*>(__curr->_M_next);
      _Node_alloc_traits::destroy(_M_get_Node_allocator(),
                      __temp->_M_valptr());
      __temp->~_Node();
      _M_put_node(__temp);
    }
      __pos->_M_next = __last;
      return __last;
    }

4.4、std::forward_list

forward_list 继承于 _Fwd_list_base 类,没有定义其他成员变量,使用 \Fwd_list_base 中对单向链表的定义。

  template<typename _Tp, typename _Alloc = allocator<_Tp>>
    class forward_list : private _Fwd_list_base<_Tp, _Alloc>
    {
    private:
      typedef _Fwd_list_base<_Tp, _Alloc>		_Base;
      typedef _Fwd_list_node_base			_Node_base;
      typedef typename _Base::_Node			_Node;
      typedef typename _Base::_Node_alloc_type		_Node_alloc_type;
      typedef typename _Base::_Node_alloc_traits	_Node_alloc_traits;
      typedef allocator_traits<__alloc_rebind<_Alloc, _Tp>>	_Alloc_traits;

    public:
      // types:
      typedef _Tp					value_type;
      typedef typename _Alloc_traits::pointer		pointer;
      typedef typename _Alloc_traits::const_pointer	const_pointer;
      typedef value_type&				reference;
      typedef const value_type&				const_reference;

      typedef typename _Base::iterator			iterator;
      typedef typename _Base::const_iterator		const_iterator;
      typedef std::size_t				size_type;
      typedef std::ptrdiff_t				difference_type;
      typedef _Alloc					allocator_type;
/// ....

forward_list 的 begin() 函数返回的迭代器指向链表第一个结点,end() 返回 nullptr。除此之外,还提供 before_begin() 函数,返回头结点,方便单向链表在插入。

/// forward_list.h
      iterator
      before_begin() noexcept
      { return iterator(&this->_M_impl._M_head); }

      iterator
      begin() noexcept
      { return iterator(this->_M_impl._M_head._M_next); }

      iterator
      end() noexcept
      { return iterator(nullptr); }

forward_list 有一个比较重要的私有成员函数是 _M_splice_after(),该函数将 (before last) 区间内的结点,插入到 pos 指向的结点后面,其实现如下。

/// forward_list.tcc
  template<typename _Tp, typename _Alloc>
    typename forward_list<_Tp, _Alloc>::iterator
    forward_list<_Tp, _Alloc>::
    _M_splice_after(const_iterator __pos,
            const_iterator __before, const_iterator __last)
    {
      _Node_base* __tmp = const_cast<_Node_base*>(__pos._M_node);
      _Node_base* __b = const_cast<_Node_base*>(__before._M_node);
      _Node_base* __end = __b;

      while (__end && __end->_M_next != __last._M_node)
    __end = __end->_M_next;

      if (__b != __end)
    return iterator(__tmp->_M_transfer_after(__b, __end));
      else
    return iterator(__tmp);
    }

比如 insert_after() 成员函数就是调用 _M_splice_after() 函数向 forward_list 中插入元素。

/// forward_list.tcc
  template<typename _Tp, typename _Alloc>
    typename forward_list<_Tp, _Alloc>::iterator
    forward_list<_Tp, _Alloc>::
    insert_after(const_iterator __pos, size_type __n, const _Tp& __val)
    {
      if (__n)
    {
      forward_list __tmp(__n, __val, get_allocator());
      return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
    }
      else
    return iterator(const_cast<_Node_base*>(__pos._M_node));
    }

  template<typename _Tp, typename _Alloc>
    template<typename _InputIterator, typename>
      typename forward_list<_Tp, _Alloc>::iterator
      forward_list<_Tp, _Alloc>::
      insert_after(const_iterator __pos,
           _InputIterator __first, _InputIterator __last)
      {
    forward_list __tmp(__first, __last, get_allocator());
    if (!__tmp.empty())
      return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
    else
      return iterator(const_cast<_Node_base*>(__pos._M_node));
      }

merge() 也是 调用 _M_splice_after() 函数。

/// forward_list.tcc
  template<typename _Tp, typename _Alloc>
    template<typename _Comp>
      void
      forward_list<_Tp, _Alloc>::
      merge(forward_list&& __list, _Comp __comp)
      {
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 3088. forward_list::merge behavior unclear when passed *this
    if (std::__addressof(__list) == this)
      return;

    _Node_base* __node = &this->_M_impl._M_head;
    while (__node->_M_next && __list._M_impl._M_head._M_next)
      {
        if (__comp(*static_cast<_Node*>
               (__list._M_impl._M_head._M_next)->_M_valptr(),
               *static_cast<_Node*>
               (__node->_M_next)->_M_valptr()))
          __node->_M_transfer_after(&__list._M_impl._M_head,
                    __list._M_impl._M_head._M_next);
        __node = __node->_M_next;
      }

    if (__list._M_impl._M_head._M_next)
      *__node = std::move(__list._M_impl._M_head);
      }

forward_list::sort() 使用归并排序,评价时间复杂度 O(NlgN)。

每轮归并的两个链表,最大长度为 insize,下一轮归并,insize *= 2,两倍增长(应为 insize 的两个链表已经合并了)。

/// forward_list.tcc
  template<typename _Tp, class _Alloc>
    template<typename _Comp>
      void
      forward_list<_Tp, _Alloc>::
      sort(_Comp __comp)
      {
    // If `next' is nullptr, return immediately.
    _Node* __list = static_cast<_Node*>(this->_M_impl._M_head._M_next);
    if (!__list)
      return;

    unsigned long __insize = 1;

    while (1)
      {
        _Node* __p = __list;
        __list = nullptr;
        _Node* __tail = nullptr;

        // Count number of merges we do in this pass.
        unsigned long __nmerges = 0; // 本轮归并合并的次数
      // 归并 p 和 q 指向的两个链表,长度分别为 psize 和 qsize
        while (__p)
          {
        ++__nmerges;
        // There exists a merge to be done.
        // Step `insize' places along from p.
        _Node* __q = __p;
        unsigned long __psize = 0; // for 循环为了确定 qsize 大小
        for (unsigned long __i = 0; __i < __insize; ++__i)
          { // 最大为 insize,可能小于 insize,应为 q 到链表末尾了
            ++__psize;
            __q = static_cast<_Node*>(__q->_M_next);
            if (!__q)
              break;
          }

        // If q hasn't fallen off end, we have two lists to merge.
        unsigned long __qsize = __insize;
    // qsize 默认为 insize,需要结合 q 判断释放不为空
        // Now we have two lists; merge them.
        while (__psize > 0 || (__qsize > 0 && __q))
          { // p 链表或者 q 链表不为空
            // Decide whether next node of merge comes from p or q.
            _Node* __e;
            if (__psize == 0)
              {
            // p is empty; e must come from q.
            __e = __q;
            __q = static_cast<_Node*>(__q->_M_next);
            --__qsize;
              }
            else if (__qsize == 0 || !__q)
              {
            // q is empty; e must come from p.
            __e = __p;
            __p = static_cast<_Node*>(__p->_M_next);
            --__psize;
              }
            else if (!__comp(*__q->_M_valptr(), *__p->_M_valptr()))
              {
            // First node of q is not lower; e must come from p.
            __e = __p;
            __p = static_cast<_Node*>(__p->_M_next);
            --__psize;
              }
            else
              {
            // First node of q is lower; e must come from q.
            __e = __q;
            __q = static_cast<_Node*>(__q->_M_next);
            --__qsize;
              }

            // Add the next node to the merged list.
            if (__tail)
              __tail->_M_next = __e;
            else
              __list = __e;
            __tail = __e;
          } // while (p || q)

        // Now p has stepped `insize' places along, and q has too.
        __p = __q; // 合并完成,从 q 开始再拆分出两个链表用于合并
          } // while (p)
        __tail->_M_next = nullptr;

        // If we have done only one merge, we're finished.
        // Allow for nmerges == 0, the empty list case.
        if (__nmerges <= 1)
          {
        this->_M_impl._M_head._M_next = __list;
        return;
          }

        // Otherwise repeat, merging lists twice the size.
        __insize *= 2; // 扩大 insize
      }
      }

欢迎关注公众号“源知源为”,阅读更多技术干货
#STL#
C/C++基础 文章被收录于专栏

C/C++ 语言基础

全部评论

相关推荐

Pandaileee:校友加油我现在也只有一个保底太难了
点赞 评论 收藏
分享
点赞 评论 收藏
分享
11-01 20:03
已编辑
门头沟学院 算法工程师
Amazarashi66:这种也是幸存者偏差了,拿不到这个价的才是大多数
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务