1. stl_list 介紹
今天我們來總結一下stl_List, 通過之前介紹單鏈表的文章,其實對鏈表的基本操作已經十分熟悉了,那對於stl_list,無非就是鏈表結構不一樣,至於其中的增刪改查的細節實現本質是一樣的,都是處理指針偏移。相比於vector,stl_List在插入和刪除的時候可以達到O(1)的時間復雜度。
stl_list是一個雙向循環鏈表,相對單鏈表來說查找效率高,無論是插入時的前插和后插,還是從后往前查找某個元素等。既然查找效率高了,自然添加,刪除和修改元素時效率也就更高。唯一一個可以稱為不足的就是每個節點需要耗費4字節指針來保存前一個節點的地址,因此如果遇到對內存要求比較苛刻的場景,而且一些操作單鏈表即可滿足,那么可以考慮使用標准庫中的forward_list(單鏈表)。stl_list雙向循環鏈表基本結構圖:
2. stl_list 源碼分析
分析gnu c++標准庫中的stl_list,我們只需把握住整體結構即可,實現總共由三部分組成,鏈表節點(struct _List_node : public __detail::_List_node_base) ,迭代器(struct _List_iterator),鏈表數據結構(class list : protected _List_base<_Tp, _Alloc>)。
stl_list uml 圖
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.迭代器,主要是實現++和--等操作符重載,實現鏈表節點的前后移動。
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