sgi_stl源码学习,官方文档3.2.2Associative Containers关联容器,hashtable源码解析

25 篇文章 0 订阅

https://www.boost.org/sgi/stl/table_of_contents.html
关联容器Associative Containers有以下8种,外加一个hash函数

以下是一组set

template <class _Key, class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Key) >
class set;

template <class _Key, class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Key) >
class multiset;

template <class _Value,
          class _HashFcn  __STL_DEPENDENT_DEFAULT_TMPL(hash<_Value>),
          class _EqualKey __STL_DEPENDENT_DEFAULT_TMPL(equal_to<_Value>),
          class _Alloc =  __STL_DEFAULT_ALLOCATOR(_Value) >
class hash_set;

template <class _Value,
          class _HashFcn  __STL_DEPENDENT_DEFAULT_TMPL(hash<_Value>),
          class _EqualKey __STL_DEPENDENT_DEFAULT_TMPL(equal_to<_Value>),
          class _Alloc =  __STL_DEFAULT_ALLOCATOR(_Value) >
class hash_multiset;

以下是一组map

template <class _Key, class _Tp, 
          class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class map;

template <class _Key, class _Tp, 
          class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class multimap;

template <class _Key, class _Tp,
          class _HashFcn  __STL_DEPENDENT_DEFAULT_TMPL(hash<_Key>),
          class _EqualKey __STL_DEPENDENT_DEFAULT_TMPL(equal_to<_Key>),
          class _Alloc =  __STL_DEFAULT_ALLOCATOR(_Tp) >
class hash_map;

template <class _Key, class _Tp,
          class _HashFcn  __STL_DEPENDENT_DEFAULT_TMPL(hash<_Key>),
          class _EqualKey __STL_DEPENDENT_DEFAULT_TMPL(equal_to<_Key>),
          class _Alloc =  __STL_DEFAULT_ALLOCATOR(_Tp) >
class hash_multimap;

hash函数的实现,string特殊实现,整数类型都是数值本身。

template <class _Key> struct hash { };//后面是各种类型的特化版本
inline size_t __stl_hash_string(const char* __s){
  unsigned long __h = 0; 
  for ( ; *__s; ++__s)
    __h = 5*__h + *__s;  //只有string类型有特殊实现
  return size_t(__h);
  }
__STL_TEMPLATE_NULL struct hash<const char*>{
  size_t operator()(const char* __s) const { return __stl_hash_string(__s); }
};
__STL_TEMPLATE_NULL struct hash<int> {
  size_t operator()(int __x) const { return __x; }//整数类型都是数值本身
};

关于allocator,实际就是简单地封装了new、delete,但也有复杂地实现版本
以下为默认版本的allocator实现

template <class T>
inline T* allocate(ptrdiff_t size, T*) {
    set_new_handler(0);
    T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
    if (tmp == 0) {
	cerr << "out of memory" << endl; 
	exit(1);
    }
    return tmp;
}
template <class T>
inline void deallocate(T* buffer) {
    ::operator delete(buffer);
}
template <class T>
class allocator {
public:
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    pointer allocate(size_type n) { 
	return ::allocate((difference_type)n, (pointer)0);
    }
    void deallocate(pointer p) { ::deallocate(p); }
    pointer address(reference x) { return (pointer)&x; }
    const_pointer const_address(const_reference x) { 
	return (const_pointer)&x; 
    }
    size_type init_page_size() { 
	return max(size_type(1), size_type(4096/sizeof(T))); 
    }
    size_type max_size() const { 
	return max(size_type(1), size_type(UINT_MAX/sizeof(T))); 
    }
};
class allocator<void> {
public:
    typedef void* pointer;
};

set的参数说明:
关键字key要求是Assignable可赋值的。
比较函数Compare要求是Strict Weak Ordering严格弱排序,参数为Key.具体可以模仿着写。
Alloc is an Allocator.

map的key、compare、alloc与set要求一致。多了一个key对应的value。
要求定义以下接口

data_type& operator[](const key_type& k)

Multiset 与set的区别在于,重复的不删除,全部存储。
Multimap 实现了一对多的映射。
Hash_set与set的区别:删了compare函数、加了hash函数和==函数,其他一样。似乎是对hashtable的封装

  typedef hashtable<_Value, _Value, _HashFcn, _Identity<_Value>, 
                    _EqualKey, _Alloc> _Ht;
  _Ht _M_ht;

_Identity有点特别。unary function 为一元函数,不做任何处理,适用于有些函数的参数(方便用户指定只是简单地拷贝,不一定非要改变)

// 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; }
};

在 STL 中,有一些算法(如 transform、for_each 等)需要一个一元函数对象作为参数,而这个函数对象有时候只是单纯地将参数原封不动地返回,此时就可以使用 _Identity。例如:

vector<int> v{1, 2, 3, 4, 5};
vector<int> v2(v.size());
// 使用 transform 将 v 中的每个元素复制到 v2 中
transform(v.begin(), v.end(), v2.begin(), _Identity<int>());
template <class _Val>
struct _Hashtable_node
{
  _Hashtable_node* _M_next;
  _Val _M_val;
};  
typedef _Hashtable_node<_Val> _Node;
private:
  hasher                _M_hash; 函数
  key_equal             _M_equals;函数
  _ExtractKey           _M_get_key;函数
  vector<_Node*,_Alloc> _M_buckets;hash桶,应该存地是首节点地指针,冲突时存成单向链表
  size_type             _M_num_elements;

计算某个桶内个数,可以看出以上猜测没问题
  size_type elems_in_bucket(size_type __bucket) const  {
    size_type __result = 0;
    for (_Node* __cur = _M_buckets[__bucket]; __cur; __cur = __cur->_M_next)
      __result += 1;
    return __result;
  }
桶的序号是通过求余得到
  size_type _M_bkt_num_key(const key_type& __key) const  {
    return _M_bkt_num_key(__key, _M_buckets.size());
  }
  size_type _M_bkt_num_key(const key_type& __key, size_t __n) const  {
    return _M_hash(__key) % __n;
  }
// Note: assumes long is at least 32 bits.
enum { __stl_num_primes = 28 };
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
  53ul,         97ul,         193ul,       389ul,       769ul,
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul, 
  1610612741ul, 3221225473ul, 4294967291ul
};
__stl_prime_list存储哈希表中的桶的数量,质数。
在 STL 中的哈希表实现中,当插入的元素数量增加时,会根据当前元素数量选择一个合适的桶的数量,这个数量就是从数组中选择的一个质数。
例如,当元素数量为 100 时,选择的桶的数量就是数组中第 7 个数,即 3079;当元素数量为 1000 时,选择的桶的数量就是数组中第 10 个数,即 24593。这些质数的选择是为了确保桶的数量足够大,同时又不至于浪费过多的空间。

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::resize(size_type __num_elements_hint){
  const size_type __old_n = _M_buckets.size();
  if (__num_elements_hint > __old_n) {
    const size_type __n = _M_next_size(__num_elements_hint);
    if (__n > __old_n) {
      vector<_Node*, _All> __tmp(__n, (_Node*)(0),
                                 _M_buckets.get_allocator());
      __STL_TRY {//搬运桶内所有数据
        for (size_type __bucket = 0; __bucket < __old_n; ++__bucket) {
          _Node* __first = _M_buckets[__bucket];
          while (__first) { //一个一个搬运,每个都计算桶号
            size_type __new_bucket = _M_bkt_num(__first->_M_val, __n);
            _M_buckets[__bucket] = __first->_M_next;
            __first->_M_next = __tmp[__new_bucket];
            __tmp[__new_bucket] = __first;
            __first = _M_buckets[__bucket];          
          }
        }
        _M_buckets.swap(__tmp);
      }
#         ifdef __STL_USE_EXCEPTIONS
      catch(...) {
        for (size_type __bucket = 0; __bucket < __tmp.size(); ++__bucket){
          while (__tmp[__bucket]) {
            _Node* __next = __tmp[__bucket]->_M_next;
            _M_delete_node(__tmp[__bucket]);
            __tmp[__bucket] = __next;
          }
        }
        throw;
      }
#         endif /* __STL_USE_EXCEPTIONS */
    }
  }
}

Hash_map 与Hash_set一样,都是对hashtable的封装。
hash_multiset、Hash_multimap 都是对hashtable的封装。
接口的细节实现上存在部分差别,估计不大,有空再看。

下一步解析set、map背后的_Rb_tree源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值