stl源码剖析_STL之set源码剖析
“ 源碼面前,了無秘密。”
之前在 小bug蘊含大能量 中講過一個和set相關的bug,說過要從紅黑樹到STL 紅黑樹,再到STL set的源碼逐步掌握整個知識架構。
最近已經把這塊的東西都學習完了,總結的時候發現并不好寫。要以什么樣的順序分享出來,也很讓人頭疼。
因為set的源碼本身看起來非常簡單,最后還是覺得應該先學會set的使用再去呈現紅黑樹的知識點,這樣大家再學習紅黑樹的時候直觀感覺也會好一點,這也是我自己學習過程中的一點體會。
因此,這次我們通過剖析set的源碼,總結set的常用方法,學會怎么使用set容器。
01
—
set源碼剖析
set的源碼是非常簡單的,包含在“stl_set.h”文件中,我們把set的源碼部分分解來看,并總結出它的特性。
第一部分:set的定義
// 模板包含三個參數/** * @tparam _Key Type of key objects. * @tparam _Compare Comparison function object type, defaults to less<_key> * @tparam _Alloc type, defaults to allocator<_key>*/template<typename _Key, typename _Compare = std::less<_key>, typename _Alloc = std::allocator<_key> >class set{ typedef typename _Alloc::value_type _Alloc_value_type; public: // key和value是同一個 typedef _Key key_type; typedef _Key value_type; typedef _Compare key_compare; typedef _Compare value_compare; typedef _Alloc allocator_type;private: // 底層的結構是紅黑樹 typedef _Rb_tree, key_compare, _Key_alloc_type> _Rep_type; _Rep_type _M_t; // Red-black tree representing set.}從上述代碼中我們可以看出
set的底層結構是紅黑樹_Rep_type _M_t;
因為是紅黑樹所以元素自動排序,排序函數為_Compare,默認按照標準函數std::less<_key>進行排序的,std::less<_key>的源碼如下
set中的元素實際上也是一個key-value對,只是key和value是一樣的
第二部分:set的迭代器
//?set的迭代器都是const“型”的// _GLIBCXX_RESOLVE_LIB_DEFECTS// DR 103. set::iterator is required to be modifiable,// but this allows modification of keys.typedef typename _Rep_type::const_iterator iterator;typedef typename _Rep_type::const_iterator const_iterator;typedef typename _Rep_type::const_reverse_iterator reverse_iterator;typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator從上述代碼可以看出,set的迭代器都是const的,因此無法通過迭代器修改set元素。
第三部分:set的構造函數
set() : _M_t() { }set(const _Compare& __comp,const allocator_type& __a = allocator_type()): _M_t(__comp, _Key_alloc_type(__a)) { }templateset(_InputIterator __first, _InputIterator __last): _M_t(){ _M_t._M_insert_unique(__first, __last); }set(const set& __x): _M_t(__x._M_t) { }set(initializer_list __l,const _Compare& __comp = _Compare(),const allocator_type& __a = allocator_type()): _M_t(__comp, _Key_alloc_type(__a)){ _M_t._M_insert_unique(__l.begin(), __l.end()); }可以使用一個[__first,__last)區間來構造,也可以利用列表初始化的形式來構造initializer_list。
另外,我們可以注意到底層都調用了紅黑樹的_M_insert_unique函數,源碼如下,可以發現底層又調用了_M_get_insert_unique_pos函數,該函數返回的是一個pair類型,如果pair的second為0則插入失敗。
template<typename _Key, typename _Val, typename _KeyOfValue, typename _Compare, typename _Alloc>#if __cplusplus >= 201103Ltemplate<typename _Arg>#endifpair<typename _Rb_tree<_key _val _keyofvalue> _Compare, _Alloc>::iterator, bool>_Rb_tree<_key _val _keyofvalue _compare _alloc>::_M_insert_unique( _Arg && __v ){ typedef pairbool> _Res; pair<_base_ptr _base_ptr> __res = _M_get_insert_unique_pos( _KeyOfValue() ( __v ) );??//?如果pair的second不為0,則執行插入 if ( __res.second ) return(_Res( _M_insert_( __res.first, __res.second, _GLIBCXX_FORWARD( _Arg, __v ) ), true ) );??// 否則返回失敗 return(_Res( iterator( static_cast<_link_type>(__res.first) ), false ) );}而_M_get_insert_unique_pos函數的源碼如下,我們看看在什么時候second為0
template<typename _Key, typename _Val, typename _KeyOfValue,typename _Compare, typename _Alloc>pair<typename _Rb_tree<_key _val _keyofvalue>_Compare, _Alloc>::_Base_ptr,typename _Rb_tree<_key _val _keyofvalue _alloc>::_Base_ptr>_Rb_tree<_key _val _keyofvalue _compare _alloc>::_M_get_insert_unique_pos(const key_type& __k){ // typedef pair typedef pair<_base_ptr _base_ptr> _Res; // _x表示當前節點,_y表示_x的父節點 _Link_type __x = _M_begin(); _Link_type __y = _M_end(); bool __comp = true; // 尋找插入點 while (__x != 0) { __y = __x; // __k<__x> __comp = _M_impl._M_key_compare(__k, _S_key(__x)); // __k<__x> __x = __comp ? _S_left(__x) : _S_right(__x); } iterator __j = iterator(__y); // __k<__y> if (__comp) { // 特殊位置 if (__j == begin()) return _Res(__x, __y); else --__j; // 左孩子 這里調用了--操作符 }????//?否則直接比較__j和__k的大小,此時的__j就是y // __j<__k> if (_M_impl._M_key_compare(_S_key(__j._M_node), __k)) return _Res(__x, __y); // _j>=__k,插入失敗 return _Res(__j._M_node, 0);}通過分析上述源代碼,可以發現滿足當k
所以,經過這么長的分析,我們可以得出這樣的結論:set中的元素都是唯一的。
02
—
set常用方法總結
如果讀懂了上面的源碼分析,學習起set的方法來接簡單多了。
begin()方法返回set的首元素,調用的是紅黑樹的begin()方法,實際上這個紅黑樹的begin()方法返回的并不是整棵樹的根節點,而是整個樹的最左節點。因為set的元素是按順序排列的,最左節點也是最小節點。
iteratorbegin() const _GLIBCXX_NOEXCEPT{?return?_M_t.begin();?}同理end()方法則返回的是最右節點,最小的值。
iteratorend() const _GLIBCXX_NOEXCEPT{ return _M_t.end(); }empty()方法判斷是否為空
boolempty() const _GLIBCXX_NOEXCEPT{ return _M_t.empty(); }insert方法有很多種,但是底層都調用的是紅黑樹的_M_insert_unique方法
std::pairbool>insert(const value_type& __x){ std::pair<typename _Rep_type::iterator, bool> __p = _M_t._M_insert_unique(__x); return std::pairbool>(__p.first, __p.second);}erase方法是刪除元素
size_typeerase(const key_type& __x){ return _M_t.erase(__x); }clear()方法用于清空所有元素
voidclear() _GLIBCXX_NOEXCEPT{ _M_t.clear(); }count方法和find方法都用于查找元素
size_typecount(const key_type& __x) const{ return _M_t.find(__x) == _M_t.end() ? 0 : 1; }iteratorfind(const key_type& __x){ return _M_t.find(__x); }總結
以上是生活随笔為你收集整理的stl源码剖析_STL之set源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python怎么输入程序代码_学习用 T
- 下一篇: sqlmap自动扫描注入点_SQLmap