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源码。