参考链接 https://blog.csdn.net/ddkxddkx/article/details/6555754
还有这个源码解析https://zrj.me/archives/1248
1.下面的代码结果是多少?
#include <unordered_map>
#include <cstring>
#include <iostream>
using namespace std;
class A // 自定义类型必须手写比较函数
{
public:
A(): val(0) {}
int get_val() const {return val;}
bool operator== (const A& b) const {return val == b.get_val();}
private:
int val;
};
struct A_hash // 自定义类型必须手写哈希函数
{
size_t operator() (const A& s) const
{
// return __stl_hash_string(s.c_str());
return s.get_val()%10;
}
};
int main()
{
A a;
unordered_map<A, int, A_hash, equal_to<A> > m;
m[a] = 1;
A b;
m[b] = 2;
cout << m[a] << endl;
return 0;
}
答案:2
为什么是2呢?
这要从unordered_map的原型来看
template<class _Key, class _Tp,
class _Hash = hash<_Key>,
class _Pred = std::equal_to<_Key>,
class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class unordered_map
unordered_map<string, int>
相当于
unordered_map<string, int, hash<int>, equal_to<int>, allocator<pair<const string, int>>>
1.哈希函数
struct hash<int> {
size_t operator()(int __x) const { return __x; }
};
如果是自定义类型,要自己实现哈希函数
2.比较函数
比较函数用于当哈希值对应的位置已经有值的时候,进行比较
3.显式具体化
hashtable.h
/// Base types for unordered_map.
template<bool _Cache>
using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;
template<typename _Key,
typename _Tp,
typename _Hash = hash<_Key>,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,
typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
using __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
_Alloc, __detail::_Select1st,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
// _Key std::pair<const _Keym _Tp>
typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
explicit
unordered_map(size_type __n,
const hasher& __hf = hasher(),
const key_equal& __eql = key_equal(),
const allocator_type& __a = allocator_type())
: _M_h(__n, __hf, __eql, __a)
{}
__umap_hashtable:
template<bool _Cache_hash_code, bool _Constant_iterators, bool _Unique_keys>
struct _Hashtable_traits
{
using __hash_cached = __bool_constant<_Cache_hash_code>;
using __constant_iterators = __bool_constant<_Constant_iterators>;
using __unique_keys = __bool_constant<_Unique_keys>;
};
_HashTable
下面是__cache_default
template<typename _Tp, typename _Hash>
using __cache_default
= __not_<__and_<// Do not cache for fast hasher.
__is_fast_hash<_Hash>,
// Mandatory to have erase not throwing.
__detail::__is_noexcept_hash<_Tp, _Hash>>>;
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits>
class _Hashtable
: public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
_H1, _H2, _Hash, _Traits>,
public __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>,
public __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>,
public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>,
public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>,
默认的哈希函数
__hash_code
_M_hash_code(const _Key& __k) const
{
// code2
// std::cout << "code2" << std::endl;
return _M_h1()(__k); }
_M_h1():
const _H1&
_M_h1() const { std::cout << "const_M_h1" << std::endl; return __ebo_h1::_S_cget(*this); }
__ebo_h1::_S_cget(*this):
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
using __ebo_h2 = _Hashtable_ebo_helper<2, _H2>;
template<int _Nm, typename _Tp>
struct _Hashtable_ebo_helper<_Nm, _Tp, true>
: private _Tp
{
_Hashtable_ebo_helper() = default;
template<typename _OtherTp>
_Hashtable_ebo_helper(_OtherTp&& __tp)
: _Tp(std::forward<_OtherTp>(__tp))
{ }
static const _Tp&
_S_cget(const _Hashtable_ebo_helper& __eboh)
{ return static_cast<const _Tp&>(__eboh); }
static _Tp&
_S_get(_Hashtable_ebo_helper& __eboh)
{ return static_cast<_Tp&>(__eboh); }
};
返回一个H1,H1是什么?第六个参数
template<typename _Key,
typename _Tp,
typename _Hash = hash<_Key>,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,
typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
using __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
_Alloc, __detail::_Select1st,
_Pred, _Hash, // 这是第六个参数
__detail::_Mod_range_hashing, // _M_bucket_index
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
就是用默认的哈希函数来计算键值
所以,对自定义类型,需要自己实现哈希函数与比较函数
插入:
operator[](const key_type& __k)
{ return _M_h[__k]; }
hashtable_policy.h
using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey,
_Equal, _H1, _H2, _Hash,
_RehashPolicy, _Traits>;
template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits>
auto
_Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits, true>::
operator[](const key_type& __k)
-> mapped_type&
{
__hashtable* __h = static_cast<__hashtable*>(this); // 这个只能在子类方法中使用
__hash_code __code = __h->_M_hash_code(__k); // 计算哈希值
std::size_t __n = __h->_M_bucket_index(__k, __code);
__node_type* __p = __h->_M_find_node(__n, __k, __code);
if (!__p)
{
__p = __h->_M_allocate_node(std::piecewise_construct,
std::tuple<const key_type&>(__k),
std::tuple<>());
return __h->_M_insert_unique_node(__n, __code, __p)->second;
}
return __p->_M_v().second;
}
下面来看一看_M_bucket_index
std::size_t
_M_bucket_index(const _Key&, __hash_code __c, std::size_t __n) const
{
//std::cout << "bucket2" << std::endl;
return _M_h2()(__c, __n); }
看一看_M_h2:
const _H2&
_M_h2() const { return __ebo_h2::_S_cget(*this); }
_H2&
_M_h2() { return __ebo_h2::_S_get(*this); }
返回了 H2
struct _Mod_range_hashing
{
typedef std::size_t first_argument_type;
typedef std::size_t second_argument_type;
typedef std::size_t result_type;
result_type
operator()(first_argument_type __num,
second_argument_type __den) const noexcept
{
return __num % __den;
}
};
如果两个key相等的时候,不用插入
不相等,则在桶里面插入
查找:
两个key相等放回
不相等找下一个
函数原型
template <class _Tp>
struct equal_to : public binary_function<_Tp,_Tp,bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const { return __x == __y; }
};