STL源码剖析: 第5章 关联式容器

xiaoxiao2021-02-28  101

5.1 概览

5.2 红黑树

5.2.1 代码实现的双层结构

5.2.2 红黑树的根节点的父节点的特殊设计

header的语义 left: 红黑树中最左节点 right:红黑树中最右节点 parent:红黑树原本实际根节点

实际的root的父节点为header

5.2.3 迭代器

begin() : node为最左边节点的迭代器 end() : node为header的迭代器

5.2.4 insert

5.2.5 配接能力

模板参数 class _KeyOfValue, 能够通过value值提取出其对应的key _KeyOfValue()新构造一个_KeyOfValue类型的临时对象,然后调用该对象的operator()函数得到_Key,如下图程序所示

template <class _Key, class _Value, class _KeyOfValue, class _Compare, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Value) > class _Rb_tree : protected _Rb_tree_base<_Value, _Alloc>{ static const _Key& _S_key(_Link_type __x) { return _KeyOfValue()(_S_value(__x)); } }

注意红黑树的的KeyOfValue的含义,Value指的是键值-实值Pair对,不是单独的实值T!!!T才是真正的实值

5.3 set

5.3.1 原理

调用红黑树的接口,维持一颗红黑树即可

5.3.2 set内部红黑树模板参数的对应关系

_Rb_tree< _Key => _Key, _Value => _Key, _KeyOfValue => _Identity<_Key> _Compare => _Compare, //外部参数默认为less<_Key> _Alloc => _Alloc >

因为set要求key和value(实际上是类型T的对象p)相同,所以红黑树节点中的value域中只需要保留一份实际的类型T的对象p即可,然后通过实际的实际的类型T的对象p转化出key,然后根据key,比较key的大小来查找插入删除

template <class _Key, class _Compare, class _Alloc> class set { // requirements: __STL_CLASS_REQUIRES(_Key, _Assignable); __STL_CLASS_BINARY_FUNCTION_CHECK(_Compare, bool, _Key, _Key); public: // typedefs: typedef _Key key_type; typedef _Key value_type; typedef _Compare key_compare; typedef _Compare value_compare; private: typedef _Rb_tree<key_type, value_type, _Identity<value_type>, key_compare, _Alloc> _Rep_type; _Rep_type _M_t; // red-black tree representing set …… }

class _Identity的源码 返回了传入参数的的引用

// identity is an extensions: it is not part of the standard. template <class _Tp> struct _Identity : public unary_function<_Tp,_Tp> { const _Tp& operator()(const _Tp& __x) const { return __x; } };

5.3.3 具体例子分析

set<int, less<int>, _Alloc>

实际的内部红黑树对应关系为

_Rb_tree< _Key => int, _Value => int, _KeyOfValue => _Identity<int> _Compare => less<int>, _Alloc => _Alloc >

结合内部代码

_Compare _M_key_compare; value __v; _M_key_compare(_KeyOfValue()(__v), _S_key(__x))

等效于

less<int> _M_key_compare; int __v; _M_key_compare(_Identity<int>()(__v), _S_key(__x))

等效于如下过程

Created with Raphaël 2.1.0 start 调用_Identity<int>()构造一个临时对象_Identity<int> tmp 调用tmp.operator(__V),返回__v的引用,即int& __v,也即int p1 _S_key(__x)返回一个对象int p2 两者通过_M_key_compare.operator(int p1, int p2)进行比较 比较规则是按照_M_key_compare的类型less<int>来决定的 end

这样配接,即把实值value的类型和键值key的类型设成一样,用value来构造新的key,然后让key按照排序插入

5.4 map

5.4.1 原理

5.4.2 map内部红黑树模板参数的对应关系

_Rb_tree( _Key => _Key, _Value => pair<"const _Key, _Tp">, //map存到红黑树里面就是pair对 _KeyOfValue => _Select1st<"pair<"const _Key, _Tp">">, _Compare => _Compare,//外部参数默认为less<_Key> _Alloc => _Alloc )

map的key 和 value(实际上是类型T的对象p)是不同的,所以需要在红黑树的节点的value域中保留key p1 和T p2的pair对,下次从pair对中取出对应的key,根据key的大小查找插入排序

template <class _Key, class _Tp, class _Compare, class _Alloc> class map { public: // requirements: __STL_CLASS_REQUIRES(_Tp, _Assignable); __STL_CLASS_BINARY_FUNCTION_CHECK(_Compare, bool, _Key, _Key); // typedefs: typedef _Key key_type; typedef _Tp data_type; typedef _Tp mapped_type; typedef pair<const _Key, _Tp> value_type; typedef _Compare key_compare; class value_compare : public binary_function<value_type, value_type, bool> { friend class map<_Key,_Tp,_Compare,_Alloc>; protected : _Compare comp; value_compare(_Compare __c) : comp(__c) {} public: bool operator()(const value_type& __x, const value_type& __y) const { return comp(__x.first, __y.first); } }; private: typedef _Rb_tree<key_type, value_type, _Select1st<value_type>, key_compare, _Alloc> _Rep_type; _Rep_type _M_t; // red-black tree representing map …… } // select1st and select2nd are extensions: they are not part of the standard. template <class _Pair> struct _Select1st : public unary_function<_Pair, typename _Pair::first_type> { const typename _Pair::first_type& operator()(const _Pair& __x) const { return __x.first; } };

5.4.3 具体例子分析

map<string, int, less<string>,_Alloc>

实际的内部红黑树对应关系为

_Rb_tree< _Key => string, _Value => pair<const string, int>, //map存到红黑树里面就是pair对 _KeyOfValue => _Select1st<pair<const string, int>>, _Compare => less<string>, _Alloc => _Alloc >

结合内部代码 map插入代码

value_type 实际为 pair<const string, int> pair<iterator,bool> insert(const value_type& __x) { return _M_t.insert_unique(__x); }

红黑树插入代码

template <class _Key, class _Value, class _KeyOfValue, class _Compare, class _Alloc> pair<typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator, bool> _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc> ::insert_unique(const _Value& __v) { _Link_type __y = _M_header; _Link_type __x = _M_root(); bool __comp = true; while (__x != 0) { __y = __x; __comp = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)); __x = __comp ? _S_left(__x) : _S_right(__x); } iterator __j = iterator(__y); if (__comp) if (__j == begin()) return pair<iterator,bool>(_M_insert(__x, __y, __v), true); else --__j; if (_M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v))) return pair<iterator,bool>(_M_insert(__x, __y, __v), true); return pair<iterator,bool>(__j, false); }

其中关键代码为

_Compare _M_key_compare; value __v; _M_key_compare(_KeyOfValue()(__v), _S_key(__x))

等效于

less<string> _M_key_compare; pair<const string, int> __v; _M_key_compare(_Select1st<pair<const string, int>>()(__v), _S_key(__x))

等效于

less<string> _M_key_compare; pair<const string, int> __v; _M_key_compare(_Select1st<pair<const string, int>>().operator(__v), _S_key(__x))

等效于如下过程

Created with Raphaël 2.1.0 Start _Select1st<pair<const string, int>>().operator(__v) 取出pair<const string, int> __v 的first参数,即返回__v的 first参数 const string p1 _S_key(__x)返回一个对象 const string p2 两者通过_M_key_compare.operator(const string p1, const string p2)进行比较 比较规则是按照_M_key_compare的类型less<string>来决定的 End

这样的配接关系保证了,红黑树insert,erase的时候,都是按照键值Key的大小排序来操作的,也即string的相互大小比较决定了插入顺序 其他键值类型 和 实值类型 也是同样的道理。

小节 分析set和map中红黑树的模板参数KeyOfValue,发现这个类型参数是为了从存储到容器中的T类型参数中提取出期中的键值 1)set中,容器中只保存了value没有保存key,需要key的时候,从value中取出来作为key, 所以KeyOfValue的作用很简单,返回value本身即可 2)map中,容器中保存了<”key, value”>pair对,keyOfValue需要从这个pair对中取出其中的key参数,便于下一步比较操作。

5.4.4 map的operator[]函数

5.5 hash table

5.5.1 桶子和节点

5.5.2 迭代器

iterator begin() { for (size_type __n = 0; __n < _M_buckets.size(); ++__n) if (_M_buckets[__n]) return iterator(_M_buckets[__n], this); return end(); }

5.5.3 hash table 数据结构

5.5.4 resize

5.5.5 bkt_num

bkt_num实际上是对hash_function作了一层封装,也就是说bkt_num调用了hasher类型对象的.operator函数

实际上是对保存元素中的key关键码进行hash散列,找到对应位置,存储到容器中

size_type _M_bkt_num_key(const key_type& __key, size_t __n) const { return _M_hash(__key) % __n; }

5.5.6 实例分析

hashtable< value => int, key => int, hash-func => hash<int>, equal-key => equal_to<int>, allocator => alloc >

这里注意 1)hash-func类型用来对key作hash散列,STL内部只对几种常用类型做了hash-func处理,具体如下 对特定类型做偏特化处理

如果value的类型是除这些常用类型以外的特殊的对象类型p,那么hash-func就要重新自定义hash<”p”>,并重载operator(),传入参数类型为p型,在重载函数内部实现hash散列,也即先转为int数值型,然后再做散列,和const char* s一样的处理方式;

2)equal-key同理,如果key的类型是特殊的对象类型p,那么equal_to<”p”>需要用到类型p的判等处理,所以必须在类型p内部重载==号处理

5.5.7 hash_set

5.5.8 hash_map

转载请注明原文地址: https://www.6miu.com/read-42390.html

最新回复(0)