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++ 语言基础
