C++进阶——STL源码之顺序容器list

14 篇文章 4 订阅

顺序容器list

list为环形双向串行链表:

list的节点

以下源码的对应版本为gcc 4.9:

  /// Common part of a node in the %list. 
    struct _List_node_base
    {
      _List_node_base* _M_next;
      _List_node_base* _M_prev;

      static void
      swap(_List_node_base& __x, _List_node_base& __y) _GLIBCXX_USE_NOEXCEPT;

      void
      _M_transfer(_List_node_base* const __first,
		  _List_node_base* const __last) _GLIBCXX_USE_NOEXCEPT;

      void
      _M_reverse() _GLIBCXX_USE_NOEXCEPT;

      void
      _M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT;

      void
      _M_unhook() _GLIBCXX_USE_NOEXCEPT;
    };

  _GLIBCXX_END_NAMESPACE_VERSION
  } // namespace detail

_GLIBCXX_BEGIN_NAMESPACE_CONTAINER

  /// An actual node in the %list.
  template<typename _Tp>
    struct _List_node : public __detail::_List_node_base
    {
      ///< User's data.
      _Tp _M_data;

#if __cplusplus >= 201103L
      template<typename... _Args>
        _List_node(_Args&&... __args)
	: __detail::_List_node_base(), _M_data(std::forward<_Args>(__args)...) 
        { }
#endif
    };

从上述源码可以看出:

  •  _List_node继承自 _List_node_base,所以可以得到_list_node包含以下三个成员:

      _List_node_base* _M_next;
      _List_node_base* _M_prev;

      //< User's data.
      _Tp _M_data;

list的迭代器

_List_iterator 的实现如下:

 /**
   *  @brief A list::iterator.
   *
   *  All the functions are op overloads.
  */
  template<typename _Tp>
    struct _List_iterator
    {
      typedef _List_iterator<_Tp>                _Self;
      typedef _List_node<_Tp>                    _Node;

      typedef ptrdiff_t                          difference_type;
      typedef std::bidirectional_iterator_tag    iterator_category;
      typedef _Tp                                value_type;
      typedef _Tp*                               pointer;
      typedef _Tp&                               reference;

      _List_iterator() _GLIBCXX_NOEXCEPT
      : _M_node() { }

      explicit
      _List_iterator(__detail::_List_node_base* __x) _GLIBCXX_NOEXCEPT
      : _M_node(__x) { }

      _Self
      _M_const_cast() const _GLIBCXX_NOEXCEPT
      { return *this; }

      // Must downcast from _List_node_base to _List_node to get to _M_data.
      reference
      operator*() const _GLIBCXX_NOEXCEPT
      { return static_cast<_Node*>(_M_node)->_M_data; }

      pointer
      operator->() const _GLIBCXX_NOEXCEPT
      { return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }

      _Self&
      operator++() _GLIBCXX_NOEXCEPT
      {
	_M_node = _M_node->_M_next;
	return *this;
      }

      _Self
      operator++(int) _GLIBCXX_NOEXCEPT
      {
	_Self __tmp = *this;
	_M_node = _M_node->_M_next;
	return __tmp;
      }

      _Self&
      operator--() _GLIBCXX_NOEXCEPT
      {
	_M_node = _M_node->_M_prev;
	return *this;
      }

      _Self
      operator--(int) _GLIBCXX_NOEXCEPT
      {
	_Self __tmp = *this;
	_M_node = _M_node->_M_prev;
	return __tmp;
      }

      bool
      operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
      { return _M_node == __x._M_node; }

      bool
      operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
      { return _M_node != __x._M_node; }

      // The only member points to the %list element.
      __detail::_List_node_base* _M_node;
    };

list的迭代器包含一个成员指针_M_node,指向list链表的节点,如下所示(_M_node对应下图的node):

_List_iterator的几个主要实现:

1. 前++ 与 后 ++的实现

      _Self&
      operator++() _GLIBCXX_NOEXCEPT
      {

              //对迭代器累加1,就是前进一个节点
              //前++ 返回的是加1之后的节点,即下一个节点
              _M_node = _M_node->_M_next;
             return *this;
      }

      _Self
      operator++(int) _GLIBCXX_NOEXCEPT
      {

              //后++ 返回的是当前节点,然后在前进一个节点
              _Self __tmp = *this;
              _M_node = _M_node->_M_next;
              return __tmp;
      }

2. 前-- 和 后-- 的实现:

   _Self&
      operator--() _GLIBCXX_NOEXCEPT
      {
            _M_node = _M_node->_M_prev;
            return *this;
      }

      _Self
      operator--(int) _GLIBCXX_NOEXCEPT
      {
            _Self __tmp = *this;
            _M_node = _M_node->_M_prev;
            return __tmp;
      }

3. -> 和 。操作符的实现:

      reference
      operator*() const _GLIBCXX_NOEXCEPT
      { return static_cast<_Node*>(_M_node)->_M_data; }

      pointer
      operator->() const _GLIBCXX_NOEXCEPT
      { return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }

list的类结构

gcc4.9.0 的list的层级结构如下所示:

以下为_List_base的实现,其中类中包含了_List_impl的结构体,在结构体中含有_List_node_base _M_node 成员,就是上文中提到的list的节点。

/// See bits/stl_deque.h's _Deque_base for an explanation.
  template<typename _Tp, typename _Alloc>
    class _List_base
    {
    protected:
      // NOTA BENE
      // The stored instance is not actually of "allocator_type"'s
      // type.  Instead we rebind the type to
      // Allocator<List_node<Tp>>, which according to [20.1.5]/4
      // should probably be the same.  List_node<Tp> is not the same
      // size as Tp (it's two pointers larger), and specializations on
      // Tp may go unused because List_node<Tp> is being bound
      // instead.
      //
      // We put this to the test in the constructors and in
      // get_allocator, where we use conversions between
      // allocator_type and _Node_alloc_type. The conversion is
      // required by table 32 in [20.1.5].
      typedef typename _Alloc::template rebind<_List_node<_Tp> >::other
        _Node_alloc_type;

      typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type;

      struct _List_impl
      : public _Node_alloc_type
      {
	__detail::_List_node_base _M_node;

	_List_impl()
	: _Node_alloc_type(), _M_node()
	{ }

	_List_impl(const _Node_alloc_type& __a) _GLIBCXX_NOEXCEPT
	: _Node_alloc_type(__a), _M_node()
	{ }

#if __cplusplus >= 201103L
	_List_impl(_Node_alloc_type&& __a) _GLIBCXX_NOEXCEPT
	: _Node_alloc_type(std::move(__a)), _M_node()
	{ }
#endif
      };

      _List_impl _M_impl;

      _List_node<_Tp>*
      _M_get_node()
      { return _M_impl._Node_alloc_type::allocate(1); }

      void
      _M_put_node(_List_node<_Tp>* __p) _GLIBCXX_NOEXCEPT
      { _M_impl._Node_alloc_type::deallocate(__p, 1); }

  public:
      typedef _Alloc allocator_type;

      _Node_alloc_type&
      _M_get_Node_allocator() _GLIBCXX_NOEXCEPT
      { return *static_cast<_Node_alloc_type*>(&_M_impl); }

      const _Node_alloc_type&
      _M_get_Node_allocator() const _GLIBCXX_NOEXCEPT
      { return *static_cast<const _Node_alloc_type*>(&_M_impl); }

      _Tp_alloc_type
      _M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
      { return _Tp_alloc_type(_M_get_Node_allocator()); }

      allocator_type
      get_allocator() const _GLIBCXX_NOEXCEPT
      { return allocator_type(_M_get_Node_allocator()); }

      _List_base()
      : _M_impl()
      { _M_init(); }

      _List_base(const _Node_alloc_type& __a) _GLIBCXX_NOEXCEPT
      : _M_impl(__a)
      { _M_init(); }

#if __cplusplus >= 201103L
      _List_base(_List_base&& __x) noexcept
      : _M_impl(std::move(__x._M_get_Node_allocator()))
      {
	_M_init();
	__detail::_List_node_base::swap(_M_impl._M_node, __x._M_impl._M_node);
      }
#endif

      // This is what actually destroys the list.
      ~_List_base() _GLIBCXX_NOEXCEPT
      { _M_clear(); }

      void
      _M_clear() _GLIBCXX_NOEXCEPT;

      void
      _M_init() _GLIBCXX_NOEXCEPT
      {
        this->_M_impl._M_node._M_next = &this->_M_impl._M_node;
        this->_M_impl._M_node._M_prev = &this->_M_impl._M_node;
      }
    };

list的数据结构

list 不仅是一个双向串行,而且还是一个环状双向串行,所以只需一个节点指针,便能表示整个环状双向串行链表,如下图所示:

如果让_List_impl结构体中的_List_node_base _M_node 指向上图中的空白节点,_M_node 就能够符合STL的前闭后开区间的要求,并且只需一个标记就可以表示上述的整个链表,下面体会以下函数的实现:

      iterator
      begin() _GLIBCXX_NOEXCEPT
      { //_M_node指向空白节点,通过空白节点的_M_next可以获得list的第一个节点
             return iterator(this->_M_impl._M_node._M_next);
      }

      iterator
      end() _GLIBCXX_NOEXCEPT
      {
              //STL的规范要求容器的区间为前闭后开,end 表示的是容器最后一个元素的下一个位置,
              //所以这里end 返回的是当前的空白节点
              return iterator(&this->_M_impl._M_node);
      }

      reference
      back() _GLIBCXX_NOEXCEPT
      { //取尾节点的内容
            iterator __tmp = end();
            --__tmp;
            return *__tmp;
      }

      bool
      empty() const _GLIBCXX_NOEXCEPT
      { //空节点的判断
                return this->_M_impl._M_node._M_next == &this->_M_impl._M_node;
      }

list的构造和内存管理

构造函数

1. 无参构造函数:

      /**
       *  @brief  Creates a %list with no elements.
       */
      list()
#if __cplusplus >= 201103L
      noexcept(is_nothrow_default_constructible<_Node_alloc_type>::value)
#endif
      : _Base() { }

2. 带构造器的构造函数:

      /**
       *  @brief  Creates a %list with no elements.
       *  @param  __a  An allocator object.
       */
      explicit
      list(const allocator_type& __a) _GLIBCXX_NOEXCEPT
      : _Base(_Node_alloc_type(__a)) { }

3. 带n个元素且赋予初值的list

      /**
       *  @brief  Creates a %list with copies of an exemplar element.
       *  @param  __n  The number of elements to initially create.
       *  @param  __value  An element to copy.
       *  @param  __a  An allocator object.
       *
       *  This constructor fills the %list with @a __n copies of @a __value.
       */
      explicit
      list(size_type __n, const value_type& __value = value_type(),
       const allocator_type& __a = allocator_type())
      : _Base(_Node_alloc_type(__a))
      { _M_fill_initialize(__n, __value); }

4. 从一个范围进行初始化的list构造函数

      template<typename _InputIterator>
        list(_InputIterator __first, _InputIterator __last,
         const allocator_type& __a = allocator_type())
    : _Base(_Node_alloc_type(__a))
        { 
      // Check whether it's an integral type.  If so, it's not an iterator.
      typedef typename std::__is_integer<_InputIterator>::__type _Integral;
      _M_initialize_dispatch(__first, __last, _Integral());
    }

_M_fill_initialize的实现

在构造函数:     
explicit
      list(size_type __n, const value_type& __value = value_type(),
       const allocator_type& __a = allocator_type())中使用了_M_fill_initialize,来看下_M_fill_initialize的实现细节:

      // Called by list(n,v,a), and the range constructor when it turns out
      // to be the same thing.
      void
      _M_fill_initialize(size_type __n, const value_type& __x)
      {
            for (; __n; --__n)
                  push_back(__x);
      }

push_back的实现如下:

      /**
       *  @brief  Add data to the end of the %list.
       *  @param  __x  Data to be added.
       *
       *  This is a typical stack operation.  The function creates an
       *  element at the end of the %list and assigns the given data to
       *  it.  Due to the nature of a %list this operation can be done
       *  in constant time, and does not invalidate iterators and
       *  references.
       */
      void
      push_back(const value_type& __x)
      { this->_M_insert(end(), __x); }

_M_insert的实现:

      void
      _M_insert(iterator __position, const value_type& __x)
      {
           _Node* __tmp = _M_create_node(__x);
           __tmp->_M_hook(__position._M_node);
      }

_M_hook的实现(gcc-4.9.0\gcc-4.9.0\libstdc++-v3\src\c++98\list.cc):

    void
    _List_node_base::
    _M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT
    {
      this->_M_next = __position;
      this->_M_prev = __position->_M_prev;
      __position->_M_prev->_M_next = this;
      __position->_M_prev = this;
    }

通过以上的源码分析,可以知道_M_fill_initialize就是在尾部插入n个指定值的节点,每个节点通过_M_create_node来创建。

节点的创建与删除

节点的创建:

      _Node*
      _M_create_node(const value_type& __x)
      {
              _Node* __p = this->_M_get_node();
              __try
            {
                  _M_get_Tp_allocator().construct(std::__addressof(__p->_M_data), __x);
            }
            __catch(...)
           {
                 _M_put_node(__p);
                 __throw_exception_again;
            }
            return __p;
      }

_M_get_node,_M_put_node的实现主要就是进行内存的分配与释放:

      _List_node<_Tp>*
      _M_get_node()
      {
            return _M_impl._Node_alloc_type::allocate(1);
      }

      void
      _M_put_node(_List_node<_Tp>* __p) _GLIBCXX_NOEXCEPT
      {
            _M_impl._Node_alloc_type::deallocate(__p, 1);
      }

list的元素操作

1. 移除节点

      void
      pop_front() _GLIBCXX_NOEXCEPT
      {//删除第一个元素 
            this->_M_erase(begin());
      }

      void
      pop_back() _GLIBCXX_NOEXCEPT
      {// 删除尾部节点
            this->_M_erase(iterator(this->_M_impl._M_node._M_prev));
      }

_M_erase的实现:

// Erases element at position given.
 void
 _M_erase(iterator __position) _GLIBCXX_NOEXCEPT
 {
        __position._M_node->_M_unhook();
        _Node* __n = static_cast<_Node*>(__position._M_node);
#if __cplusplus >= 201103L
        _M_get_Node_allocator().destroy(__n);//调用配置器释放内存
#else
    _M_get_Tp_allocator().destroy(std::__addressof(__n->_M_data));
#endif
        _M_put_node(__n);//删除节点
}

_M_unhook的实现:

_M_unhook的实现看着就是个const的转换,不知道有没有其他的作用?

    void
    _List_node_base::_M_unhook() _GLIBCXX_USE_NOEXCEPT
    {
      _List_node_base* const __next_node = this->_M_next;
      _List_node_base* const __prev_node = this->_M_prev;
      __prev_node->_M_next = __next_node;
      __next_node->_M_prev = __prev_node;
    }

2. 在指定迭代器之前插入n个指定节点值的节点。

void insert(iterator __position, size_type __n, const value_type &__x) {
    list __tmp(__n, __x, get_allocator());//先构造一个含有n个节点的list,并初始化节点的值围为 __x
    splice(__position, __tmp);
}

splice的实现:

splice(iterator __position, list& __x)
{
    if (!__x.empty())
      {
        _M_check_equal_allocators(__x);

        this->_M_transfer(__position._M_const_cast(),
                  __x.begin(), __x.end());
      }

}

_M_transfer的实现:

      // Moves the elements from [first,last) before position.
      void
      _M_transfer(iterator __position, iterator __first, iterator __last)
      { __position._M_node->_M_transfer(__first._M_node, __last._M_node); }

_List_node_base::_M_transfer的实现(这里注意first与last表示的区间是 前闭后开):

void
_List_node_base::
_M_transfer(_List_node_base * const __first,
_List_node_base * const __last) _GLIBCXX_USE_NOEXCEPT
{
      if (this != __last)
    {
      // Remove [first, last) from its old position.
      __last->_M_prev->_M_next  = this;
      __first->_M_prev->_M_next = __last;
      this->_M_prev->_M_next    = __first;

      // Splice [first, last) into its new position.
      _List_node_base* const __tmp = this->_M_prev;
      this->_M_prev                = __last->_M_prev;
      __last->_M_prev              = __first->_M_prev;
      __first->_M_prev             = __tmp;
    }
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
神书-STL实现原理,对于强化数据结构-算法的程序员必备、必读书籍。The Best-Selling Programmer Resource–Now Updated for C++11 The C++ standard library provides a set of common classes and interfaces that greatly extend the core C++ language. The library, however, is not self-explanatory. To make full use of its components - and to benefit from their power - you need a resource that does far more than list the classes and their functions. The C++ Standard Library - A Tutorial and Reference, 2nd Edition describes this library as now incorporated into the new ANSI/ISO C++ language standard (C++11). The book provides comprehensive documentation of each library component, including an introduction to its purpose and design; clearly written explanations of complex concepts; the practical programming details needed for effective use; traps and pitfalls; the exact signature and definition of the most important classes and functions; and numerous examples of working code. The book focuses on the Standard Template Library (STL), examining containers, iterators, function objects, and STL algorithms. You will also find detailed coverage of strings, concurrency, random numbers and distributions, special containers, numerical classes, internationalization, and the IOStreams library. An insightful introduction to fundamental concepts and an overview of the library will help bring newcomers quickly up to speed. A comprehensive index will support the C++ programmer in his/her day-to-day life.
对于C++ STL源码分析,这是一个广泛而复杂的话题。C++ STLC++标准库中的一部分,包含了许多不同的容器、算法和迭代器等组件,用于提供通用的数据结构和算法支持。 在进行源码分析之前,你需要具备一定的C++编程知识和理解C++模板的工作原理。然后,你可以通过查看STL源码实现来深入了解其内部机制。 在C++ 11中,STL引入了一些新的特性和容器。例如,引用中提到的range-based for循环语句,可以更方便地遍历容器中的元素。此外,C++ 11还对容器进行了分类,包括序列容器、关联容器和无序容器等。 引用提到了STL的六个主要部分,包括容器、算法、迭代器、函数对象、适配器和分配器。这些部分提供了不同的功能和特性,可以满足各种编程需求。 在C++ 11中,一些容器名称发生了变化,如slist被重命名为forward_list,hash_set和hash_map被重命名为unordered_set和unordered_map。这些变化是为了更好地反映容器的功能和语义。 要深入了解STL源码,你可以参考一些重要的资源网站,如cplusplus.com、cppreference.com和gcc.gnu.org。这些网站提供了详细的文档和例子,以帮助你理解STL的实现细节。 总之,要进行C++ STL源码分析,你需要具备一定的编程和模板知识,并参考相关的文档和资源。通过深入研究STL源码实现,你将能够更好地理解其内部机制和使用方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值