C++源码剖析——forward_list

  前言:之前看过侯老师的《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

在这里插入图片描述

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`forward_list`是C++ STL的一个单向链表容器。单向链表是一种数据结构,它具有一些优点,如在链表中添加或删除元素更加高效。在使用`forward_list`时,必须使用迭代器来访问元素,而不是像在`vector`或`array`中那样使用下标。以下是`forward_list`的一些基本操作: 1.创建`forward_list`: ```c++ #include <forward_list> #include <iostream> using namespace std; int main() { forward_list<int> f1; // 空的forward_list forward_list<int> f2(3); // 3个元素的forward_list,元素默认值为0 forward_list<int> f3(2, 1); // 2个元素的forward_list,元素默认值为1 forward_list<int> f4{ 1, 2, 3 }; // 3个元素的forward_list,初始化列表为1、2、3 forward_list<int> f5(f4.begin(), f4.end()); // 从迭代器初始化forward_list return 0; } ``` 2.在`forward_list`中插入元素: ```c++ #include <forward_list> #include <iostream> using namespace std; int main() { forward_list<int> f{ 1, 2, 3 }; f.push_front(0); // 在前面插入0 f.insert_after(f.begin(), 4); // 在第一个元素之后插入4 f.insert_after(f.begin(), 2, 5); // 在第一个元素之后插入两个5 f.insert_after(f.begin(), {6, 7, 8}); // 在第一个元素之后插入3个元素 return 0; } ``` 3.在`forward_list`中删除元素: ```c++ #include <forward_list> #include <iostream> using namespace std; int main() { forward_list<int> f{ 1, 2, 3 }; f.pop_front(); // 删除第一个元素 f.erase_after(f.begin()); // 删除第二个元素 f.remove(3); // 删除所有等于3的元素 return 0; } ``` 4.在`forward_list`中查找元素: ```c++ #include <forward_list> #include <iostream> using namespace std; int main() { forward_list<int> f{ 1, 2, 3 }; auto it = find(f.begin(), f.end(), 3); if (it != f.end()) cout << "3 is in the forward_list." << endl; else cout << "3 is not in the forward_list." << endl; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值