日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stl源码剖析_STL之set源码剖析

發(fā)布時間:2024/9/15 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stl源码剖析_STL之set源码剖析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

源碼面前,了無秘密。

之前在 小bug蘊含大能量 中講過一個和set相關(guān)的bug,說過要從紅黑樹到STL 紅黑樹,再到STL set的源碼逐步掌握整個知識架構(gòu)。

最近已經(jīng)把這塊的東西都學(xué)習(xí)完了,總結(jié)的時候發(fā)現(xiàn)并不好寫。要以什么樣的順序分享出來,也很讓人頭疼。

因為set的源碼本身看起來非常簡單,最后還是覺得應(yīng)該先學(xué)會set的使用再去呈現(xiàn)紅黑樹的知識點,這樣大家再學(xué)習(xí)紅黑樹的時候直觀感覺也會好一點,這也是我自己學(xué)習(xí)過程中的一點體會。

因此,這次我們通過剖析set的源碼,總結(jié)set的常用方法,學(xué)會怎么使用set容器。

01

set源碼剖析

set的源碼是非常簡單的,包含在“stl_set.h”文件中,我們把set的源碼部分分解來看,并總結(jié)出它的特性。

第一部分:set的定義

// 模板包含三個參數(shù)/** * @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: // 底層的結(jié)構(gòu)是紅黑樹 typedef _Rb_tree, key_compare, _Key_alloc_type> _Rep_type; _Rep_type _M_t; // Red-black tree representing set.}

從上述代碼中我們可以看出

  • set的底層結(jié)構(gòu)是紅黑樹_Rep_type _M_t;

  • 因為是紅黑樹所以元素自動排序,排序函數(shù)為_Compare,默認(rèn)按照標(biāo)準(zhǔn)函數(shù)std::less<_key>進行排序的,std::less<_key>的源碼如下

template<typename _Tp>struct less : public binary_function<_tp _tp>bool>{ bool operator()(const _Tp& __x, const _Tp& __y) const{ return __x < __y; }}當(dāng)然也可以傳入自定義的排序函數(shù)。但是,如果要使用默認(rèn)的函數(shù),對于 ? ? ? 自定義類型就必須重載“
  • 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的構(gòu)造函數(shù)

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)區(qū)間來構(gòu)造,也可以利用列表初始化的形式來構(gòu)造initializer_list。

另外,我們可以注意到底層都調(diào)用了紅黑樹的_M_insert_unique函數(shù),源碼如下,可以發(fā)現(xiàn)底層又調(diào)用了_M_get_insert_unique_pos函數(shù),該函數(shù)返回的是一個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,則執(zhí)行插入 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函數(shù)的源碼如下,我們看看在什么時候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表示當(dāng)前節(jié)點,_y表示_x的父節(jié)點 _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; // 左孩子 這里調(diào)用了--操作符 }????//?否則直接比較__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);}

通過分析上述源代碼,可以發(fā)現(xiàn)滿足當(dāng)k

所以,經(jīng)過這么長的分析,我們可以得出這樣的結(jié)論:set中的元素都是唯一的。

02

set常用方法總結(jié)

如果讀懂了上面的源碼分析,學(xué)習(xí)起set的方法來接簡單多了。

begin()方法返回set的首元素,調(diào)用的是紅黑樹的begin()方法,實際上這個紅黑樹的begin()方法返回的并不是整棵樹的根節(jié)點,而是整個樹的最左節(jié)點。因為set的元素是按順序排列的,最左節(jié)點也是最小節(jié)點。

iteratorbegin() const _GLIBCXX_NOEXCEPT{?return?_M_t.begin();?}

同理end()方法則返回的是最右節(jié)點,最小的值。

iteratorend() const _GLIBCXX_NOEXCEPT{ return _M_t.end(); }

empty()方法判斷是否為空

boolempty() const _GLIBCXX_NOEXCEPT{ return _M_t.empty(); }

insert方法有很多種,但是底層都調(diào)用的是紅黑樹的_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); }

總結(jié)

以上是生活随笔為你收集整理的stl源码剖析_STL之set源码剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。