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

来源:互联网 发布:pr淘宝主图视频比特率 编辑:程序博客网 时间:2024/05/17 01:45

5.1 概览

title

5.2 红黑树

5.2.1 代码实现的双层结构

title

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

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

实际的root的父节点为header
title
title
title
title

5.2.3 迭代器

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

5.2.4 insert

title

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才是真正的实值
title

5.3 set

5.3.1 原理

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

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

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

title
因为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.0start调用_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 原理

title
title

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)

title
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.0Start_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[]函数

title
title

5.5 hash table

5.5.1 桶子和节点

title

5.5.2 迭代器

title
title

  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 数据结构

title
title
title

5.5.4 resize

title
title
title

5.5.5 bkt_num

title
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>

title
title
title

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

如果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

title
title
title
title

5.5.8 hash_map

title
title

阅读全文
0 0
原创粉丝点击