STL源碼分析與實現-stl_list容器

1. stl_list 介紹node

今天咱們來總結一下stl_List, 經過以前介紹單鏈表的文章,其實對鏈表的基本操做已經十分熟悉了,那對於stl_list,無非就是鏈表結構不同,至於其中的增刪改查的細節實現本質是同樣的,都是處理指針偏移。相比於vector,stl_List在插入和刪除的時候能夠達到O(1)的時間複雜度ios

stl_list是一個雙向循環鏈表,相對單鏈表來講查找效率高,不管是插入時的前插和後插,仍是從後往前查找某個元素等。既然查找效率高了,天然添加,刪除和修改元素時效率也就更高。惟一一個能夠稱爲不足的就是每一個節點須要耗費4字節指針來保存前一個節點的地址,所以若是遇到對內存要求比較苛刻的場景,並且一些操做單鏈表便可知足,那麼能夠考慮使用標準庫中的forward_list(單鏈表)。stl_list雙向循環鏈表基本結構圖:c++

2. stl_list 源碼分析git

分析gnu c++標準庫中的stl_list,咱們只需把握住總體結構便可,實現總共由三部分組成,鏈表節點(struct _List_node : public __detail::_List_node_base) ,迭代器(struct _List_iterator),鏈表數據結構(class list : protected _List_base<_Tp, _Alloc>)。github

stl_list uml 圖bash

gnu下最新版本的stl_list實現加了一些額外的繼承關係,_list_base中保存了一個_List_impl _M_impl中間變量,由該類_M_impl來保存節點,並對節點作基本處理。爲了更好的理解,咱們看面這個uml圖便可。數據結構

1.鏈表節點,父類維護兩個指針,子類才加入具體的value。函數

struct _List_node_base
    {
      _List_node_base* _M_next;
      _List_node_base* _M_prev;

    };

      template<typename _Tp>
    struct _List_node : public __detail::_List_node_base
    {
      ///< User's data.
      _Tp _M_data;

    };

2.迭代器,主要是實現++和--等操做符重載,實現鏈表節點的先後移動。oop

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;    //本質是鏈表節點的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;  //本質是鏈表節點的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; //維護一個鏈表節點
    };

3.鏈表數據結構源碼分析

實現類 _List_impl,主要用來維護鏈表節點,而後list類包含該類。

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_base類

template<typename _Tp, typename _Alloc>
    class _List_base
    {
    protected:

      typedef typename _Alloc::template rebind<_List_node<_Tp> >::other  _Node_alloc_type;

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

      static size_t
      _S_distance(const __detail::_List_node_base* __first,
		  const __detail::_List_node_base* __last)
      {
	size_t __n = 0;
	while (__first != __last)
	  {
	    __first = __first->_M_next;
	    ++__n;
	  }
	return __n;
      }

      _List_impl _M_impl;    // 中間層類

      // count the number of nodes
      size_t _M_node_count() const
      {
	return _S_distance(_M_impl._M_node._M_next,
			   std::__addressof(_M_impl._M_node));
      }


  public:
      typedef _Alloc allocator_type;

      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;
	_M_set_size(0);
      }
    };

list類:

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class list : protected _List_base<_Tp, _Alloc>
    {
      // concept requirements
      typedef typename _Alloc::value_type                _Alloc_value_type;
      __glibcxx_class_requires(_Tp, _SGIAssignableConcept)
      __glibcxx_class_requires2(_Tp, _Alloc_value_type, _SameTypeConcept)

      typedef _List_base<_Tp, _Alloc>                    _Base;
      typedef typename _Base::_Tp_alloc_type		 _Tp_alloc_type;
      typedef typename _Base::_Node_alloc_type		 _Node_alloc_type;

    public:
      typedef _Tp                                        value_type;
      typedef typename _Tp_alloc_type::pointer           pointer;
      typedef typename _Tp_alloc_type::const_pointer     const_pointer;
      typedef typename _Tp_alloc_type::reference         reference;
      typedef typename _Tp_alloc_type::const_reference   const_reference;
      typedef _List_iterator<_Tp>                        iterator;
      typedef _List_const_iterator<_Tp>                  const_iterator;
      typedef std::reverse_iterator<const_iterator>      const_reverse_iterator;
      typedef std::reverse_iterator<iterator>            reverse_iterator;
      typedef size_t                                     size_type;
      typedef ptrdiff_t                                  difference_type;
      typedef _Alloc                                     allocator_type;

    protected:
      // Note that pointers-to-_Node's can be ctor-converted to
      // iterator types.
      typedef _List_node<_Tp>				 _Node;

      using _Base::_M_impl;
      using _Base::_M_put_node;
      using _Base::_M_get_node;
      using _Base::_M_get_Tp_allocator;
      using _Base::_M_get_Node_allocator;

       ..........................................................

}

大概截取了stl_list實現的一部分,主要爲了體現stl_list的代碼結構,具體接口實現能夠查看源碼。

3. stl_list 使用和簡單實現

基本實現代碼:

#include "stl_def.h"

/** @file stl_list.h
 * 
 *  This is an stl_list header file, implement double loop list warppes  
 * 
 *  Created by yejy on 18-8-18
 *  copyright (c) yejy. all rights reserved
 * 
 */

__YAMI_BEGIN


/* stl list  allocate 直接使用默認new/delete */
template <typename T>
class list
{
public:
  // 不包數據實體,只包含指針和相關操做, 能夠認爲是節省一個指針大小的內存
  struct list_node_base  
  {
    list_node_base* Next;
    list_node_base* Prev;

    list_node_base():Next(nullptr), Prev(nullptr){}
  };

  // dataEntry node
  struct list_node: public list_node_base
  {
      T dataEntry;
  };

  // 迭代器 iterator
  struct list_iterator
  {
    typedef list_iterator   _Self;
    typedef T               value_type;
    typedef T*              pointer;
    typedef T&              reference;

    list_iterator() _T_STD_NOEXCEPT
    {
      m_smartPtr = nullptr;
    }

    explicit list_iterator(list_node_base * pNode) _T_STD_NOEXCEPT
    {
      m_smartPtr = pNode;
    }

    reference operator*() _T_STD_NOEXCEPT
    {
      return  static_cast<list_node *>(m_smartPtr)->dataEntry;
    }

    list_node_base* operator->() _T_STD_NOEXCEPT
    {
      return m_smartPtr;
    }

    _Self operator++(int) _T_STD_NOEXCEPT // 後 ++
    {
      _Self __tmp = *this;
      m_smartPtr = m_smartPtr->Next;
      return __tmp;
    }

    _Self& operator++() _T_STD_NOEXCEPT // 前 ++
    {
      m_smartPtr = m_smartPtr->Next;
      return *this;
    }

    _Self operator--(int) _T_STD_NOEXCEPT
    {
      _Self __tmp = *this;
      m_smartPtr = m_smartPtr->Prev;
      return __tmp;
    }

    _Self& operator--() _T_STD_NOEXCEPT
    {
      m_smartPtr = m_smartPtr->Prev;
      return *this;
    }

    bool operator==(const list_iterator & _Right) const _T_STD_NOEXCEPT
    {
      return m_smartPtr == _Right.m_smartPtr;
    }

    bool operator!=(const list_iterator & _Right) const _T_STD_NOEXCEPT
    {
       return m_smartPtr != _Right.m_smartPtr;
    }

    list_node_base * m_smartPtr; // 節點指針
  };

public:
  typedef list_iterator iterator;

public:
  list()  // 默認構造
  { 
    empty_init();
  }

  list(const list<T> & rhs) // 拷貝構造
  {
    if(this != &rhs)
    {
      empty_init(); // 初始化

      iterator itrBegin = rhs.begin();
      iterator itrEnd = rhs.end();

      while(itrBegin != itrEnd)
      {
         list_node * tmp = static_cast<list_node *>(itrBegin.m_smartPtr);

         push_back(tmp->dataEntry);

         ++itrBegin;
      }
    }
  }

  list & operator = (const list<T> & rhs) // 賦值運算符重載
  {
    if(this != &rhs)
    {
      // 若是原來鏈表有值,則先清空
      if(begin() != end())
      {
        clear();
      }

      iterator itrBegin = rhs.begin();
      iterator itrEnd = rhs.end();

      while(itrBegin != itrEnd)
      {
         list_node * tmp = static_cast<list_node *>(itrBegin.m_smartPtr);

         push_back(tmp->dataEntry);

         ++itrBegin;
      }
    }
  }

  ~list()
  {
    clear();

    if(pHeadNode)
    {
      delete pHeadNode;
      pHeadNode = nullptr;
    }
  }

  iterator begin() _T_STD_NOEXCEPT
  {
    return iterator(pHeadNode->Next);
  }

  iterator end() _T_STD_NOEXCEPT
  {
    return iterator(pHeadNode);
  }

  void push_back(const T & value)
  {
    insert(end(), value);
  }

  void push_front(const T & value)
  {
    insert(begin(), value);
  }

  void pop_front() 
  {
     erase(begin()); 
  }

  void pop_back() 
  { 
    iterator tmp = end();
    erase(--tmp);
  }

  T & front()
  {
    return *begin();
  }

  T & back()
  {
    return *(--end());
  }

  unsigned int remove(const T & value)
  {
    unsigned int count = 0;

    iterator itrBegin = begin();
    while(itrBegin != end())
    {
      if(*itrBegin == value)
      {
        itrBegin = erase(itrBegin);
        ++count;
      }
      else
      {
        ++itrBegin;
      }
    }

    return count;
  }

  iterator erase(iterator position)
  {
    list_node_base* next_node = position.m_smartPtr->Next;
    list_node_base* prev_node = position.m_smartPtr->Prev;
    prev_node->Next = next_node;
    next_node->Prev = prev_node;

    delete position.m_smartPtr;
    position.m_smartPtr = nullptr;
    
    if(_size > 0)
    {
      _size--;
    }

    return iterator(next_node);
  }

  iterator insert(iterator position, const T& x) 
  {
    list_node* tmp = new list_node();
    tmp->dataEntry = x;
    tmp->Next = position.m_smartPtr;
    tmp->Prev = position.m_smartPtr->Prev;
    position.m_smartPtr->Prev->Next = tmp;
    position.m_smartPtr->Prev = tmp;

    ++_size;
    return iterator(tmp);
  }

  void clear()
  {
    iterator itrBegin = begin();
    while(itrBegin != end())
    {
      list_node* tmp =  static_cast<list_node *>(itrBegin.m_smartPtr);

      ++itrBegin;

      if(tmp)
      {
        delete tmp; // 差點犯了一個錯誤,delete會對用析構函數,而且釋放內存。 須要析構子類仍是父類,必定要傳入正確類型
      }
    }

    pHeadNode->Next = pHeadNode;
    pHeadNode->Prev = pHeadNode;
    _size = 0;
  }

  int size()
  {
    return _size;
  }

private:
  void empty_init() 
  { 
    pHeadNode = new list_node_base();
    pHeadNode->Next = pHeadNode;  // 初始化指針指向本身
    pHeadNode->Prev = pHeadNode;

    _size = 0;
  }

private:
  list_node_base* pHeadNode; // 鏈表頭

  unsigned int _size; // 鏈表個數,提升查找效率,若是想爲了節省內存,能夠不要,臨時查找
};

__YAMI_END

測試代碼:

#include "stl_list.h"
#include <iostream>

class Test
{
public:
	Test()
	{
		std::cout << "construct.." << std::endl;
	}

	void method()
	{
		std::cout << "welcome Test.." << std::endl;
	}

	~Test()
	{
		std::cout << "destruct.." << std::endl;
	}
};

void printfList(Yami::list<int> & list_INT)
{
    Yami::list<int>::list_iterator itrBegin = list_INT.begin();

    while(itrBegin != list_INT.end())
    {
        std::cout << *itrBegin;
        itrBegin++;
    }

    std::cout << std::endl;
}

int main(int argc, char * argv[])
{
    std::cout << "Test bdgin !" << std::endl;
    // test int 
    Yami::list<int> list_INT;
    list_INT.push_back(1);
    list_INT.push_back(2);
    list_INT.push_back(3);
    list_INT.push_back(4);
    list_INT.push_back(5);
    list_INT.push_back(2);

    printfList(list_INT);

    std::cout << "delete nums: "<< list_INT.remove(2) << std::endl;

    printfList(list_INT);

    Yami::list<int> list_INT1;
    list_INT1.push_front(1);
    list_INT1.push_front(2);
    list_INT1.push_front(3);
    list_INT1.push_front(4);
    list_INT1.push_front(5);

    printfList(list_INT1);

    std::cout << "front: "<< list_INT1.front()<< std::endl;

    std::cout << "back: " << list_INT1.back()<< std::endl;

    list_INT1.pop_back();

    list_INT1.pop_front();

    std::cout << "size: " << list_INT1.size()<< std::endl;

    printfList(list_INT1);

    // test class  主要看一下資源析構狀況
    Test test1;
    Test test2;
    Test test3;
    Yami::list<Test> list_CLASS;
    list_CLASS.push_back(test1);
    list_CLASS.push_back(test2);
    list_CLASS.push_back(test3);

    std::cout << list_CLASS.size() << std::endl;

    list_CLASS.clear();
    
    std::cout << list_CLASS.size() << std::endl;

    // test string
    Yami::list<std::string> list_STRING;

    list_STRING.push_back("nihao");
    list_STRING.push_back("thanks");
    list_STRING.push_back("goodbye");
    list_STRING.push_back("seeyou");

    Yami::list<std::string>::list_iterator itBegin = list_STRING.begin();

    while(itBegin != list_STRING.end())
    {
        std::cout << " "<< (*itBegin).c_str();
        itBegin++;
    }

    std::cout << std::endl;

    std::cout << "Test end !" << std::endl;
    return 0;
}

測試結果:

bash-4.2$ ./stl_list 
Test bdgin !
123452
delete nums: 2
1345
54321
front: 5
back: 1
size: 3
432
construct..
construct..
construct..
construct..
construct..
construct..
3
destruct..
destruct..
destruct..
0
 nihao thanks goodbye seeyou
Test end !
destruct..
destruct..
destruct..

4. 總結

本身參考標準庫中的stl源碼實現了一個stl_list。 總的來講,stl_list實現相對簡單,迭代器專門負責元素的遍歷查找,主要實現++,--,*,->等運算符重載;list類實現循環雙向鏈表的初始化,插入和刪除操做,若是涉及到查找,則使用迭代器完成!

實現源碼參考:stl_implement

2018/9/22 20:46:44

相關文章
相關標籤/搜索