C++ 用哈希表封装unordered_set/unordered_map
前言
一、源码结构分析
二、模拟实现unordered_set/unordered_map
2.1 套上KeyOfT
2.2 普通迭代器
2.3 const迭代器
2.4 解决Key不能修改的问题
2.5 unordered_map的[]实现
三、unordered_set/unordered_map以及哈希表源码
3.1 HashTable.h
3.2 unordered_set
3.3 unordered_map
总结
前言
本篇文章我们将使用哈希表来封装unordered_set/unordered_map。但是大家要注意,在实现前一定要先保证自己的哈希表是能跑才可以来封装,而且我们的哈希表解决冲突的方式是哈希桶,并不是线性探测法。
一、源码结构分析
// stl_hash_set.h
template <class Value, class HashFcn, class EqualKey, class Alloc = alloc>
class hash_set
{
private:typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht;ht rep;public:typedef typename ht::key_type key_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::size_type size_type;typedef typename ht::difference_type difference_type;typedef typename ht::const_pointer pointer;typedef typename ht::const_pointer const_pointer;typedef typename ht::const_reference reference;typedef typename ht::const_reference const_reference;typedef typename ht::const_iterator iterator;typedef typename ht::const_iterator const_iterator;// stl_hash_map.h
template <class Key, class T, class HashFcn, class EqualKey, class Alloc = alloc>
class hash_map
{
private:typedef hashtable<pair<const Key, T>, Key, HashFcn,select1st<pair<const Key, T>>, EqualKey, Alloc> ht;ht rep;public:typedef typename ht::key_type key_type;typedef T data_type;typedef T mapped_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::size_type size_type;typedef typename ht::difference_type difference_type;typedef typename ht::pointer pointer;typedef typename ht::const_pointer const_pointer;typedef typename ht::reference reference;typedef typename ht::const_reference const_reference;typedef typename ht::iterator iterator;typedef typename ht::const_iterator const_iterator;// stl_hashtable.h
template <class Value, class Key, class HashFcn,class ExtractKey, class EqualKey,class Alloc>
class hashtable {
public:typedef Key key_type;typedef Value value_type;typedef HashFcn hasher;typedef EqualKey key_equal;typedef size_t size_type;typedef ptrdiff_t difference_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef const value_type& const_reference;template <class Value>
struct __hashtable_node
{__hashtable_node* next;Value val;
};

namespace hx
{template<class K>class unordered_set{public:private:hash_bucket::HashTable<K, K> _ht;};
}
namespace hx
{template<class K, class V>class unordered_map{public:private:hash_bucket::HashTable<K, pair<K, V>> _ht;};
}
namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}};template<class K, class T>class HashTable{typedef HashNode<T> Node;public:private:vector<Node*> _table;size_t _n = 0;};
}
二、模拟实现unordered_set/unordered_map
模拟实现一共分为五步:
1、套上KeyOfT
2、普通迭代器
3、const迭代器
4、解决Key不能修改的问题
5、unordered_map的[]实现
2.1 套上KeyOfT
在这里的insert中,要进行取模操作,如果是key就没问题,如果是pair的话,因为pair的value是不参与计算的,所以要用一个仿函数先把key给取出来,而下层HashTable由于是泛型的原因,并不知道节点中存的是什么,但是上层知道,所以这个仿函数要从上层传进来。
namespace hx
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}public:bool insert(const K& key){return _ht.insert(key);}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}
namespace hx
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}public:bool insert(const pair<K, V>& kv){return _ht.insert(kv);}private:hash_bucket::HashTable<K, K, MapKeyOfT> _ht;};
}
namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}};template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>class HashTable{typedef HashNode<T> Node;public:bool insert(const T& data){KeyOfT kot;Hash hs;if (find(kot(data)))return false;// 扩容if (_n / _table.size() == 1){vector<Node*> newtable(__stl_next_prime(_table.size() + 1), nullptr);for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newtable.size();cur->_next = newtable[hashi];newtable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newtable);}size_t hashi = hs(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return true;}};
}
这里套了两层仿函数,kot先把key取出来,hs再把key映射成整形
注:__stl_next_prime是一个有关于扩容的函数,在上一篇哈希表中有讲到,在这里为了省略一点篇幅就没有加上,在最后源码部分会加。
2.2 普通迭代器
iterator实现的大体框架跟list的iterator思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意的是哈希表的迭代器是单向迭代器。这里的难点是operator++的实现。iterator中有一个指向结点的指针,如果当前桶下面还有结点, 则结点的指针指向下一个结点即可。如果当前桶走完了,则需要想办法计算找到下一个桶。但是迭代器里面只有一个节点的指针,那怎么才能找到下一个桶呢?实际上这里只有一个节点指针是不够的。还需要有一个哈希表指针,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可。begin()返回第一个不为空的桶中第一个节点指针构造的迭代器。
总结一下,如果下一个结点不为空,就走到下一个结点,如果下一个结点为空,就找下一个不为空的桶,如果把表都找完了,还没找到下一个不为空的桶,那就用空指针去构造迭代器就可以,end()的结束我们定义成走到空就是结束。

it在24,下一个要访问的结点是13
it在13,下一个要访问的结点是96,需要先计算出当前在几号桶,再往后找第一个不为空的桶的第一个结点。
template<class K, class T, class KeyOfT, class Hash>
struct __HashTable_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HashTable_Iterator(Node* node, HT* ht):_node(node), _ht(ht){}Self& operator++(){// 当前桶没走完if (_node->_next){_node = _node->_next;}// 当前桶走完了else{KeyOfT kot;Hash hs;// 计算当前是几号桶size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();// 从下一个桶开始hashi++;while (hashi < _ht->_table.size()){if (_ht->_table[hashi]){_node = _ht->_table[hashi];break;}else{hashi++;}}if (hashi == _ht->_table.size()){_node = nullptr;}}return *this;}
};
而又因为哈希表的迭代器是单向迭代器,所以没有--操作,我们把剩余的功能补全即可。
template<class K, class T, class KeyOfT, class Hash>
struct __HashTable_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HashTable_Iterator(Node* node, HT* ht):_node(node), _ht(ht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self& operator++(){// 当前桶没走完if (_node->_next){_node = _node->_next;}// 当前桶走完了else{KeyOfT kot;Hash hs;// 计算当前是几号桶 size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();// 从下一个桶开始hashi++;while (hashi < _ht->_table.size()){if (_ht->_table[hashi]){_node = _ht->_table[hashi];break;}else{hashi++;}}if (hashi == _ht->_table.size()){_node = nullptr;}}return *this;}Self operator++(int){Self tmp = *this;++*this;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{typedef HashNode<T> Node;
public:typedef __HashTable_Iterator<K, T, KeyOfT, Hash> iterator;
现在当大家测试代码的时候会跑不通,这个测试代码不是写完了直接编译,因为编译器会按需编译,如果没有用到迭代器就不会去编译迭代器的部分,所以我们要通过迭代器访问数据来测试。这时大家会发现是编译报错的,编译报错的原因是我们在__HashTable_Iterator这个类中用到了HashTable这个类,但是又因为编译器查找的规则是向上查找,向上找不到HashTable,就报错了,那我们把HashTable类放在上面可以吗?答案是也不行,因为HashTable中又要用到__HashTable_Iterator,这两个类是互相依赖的关系,不管谁放到上面,另一个都会找不到。所以我们需要前置声明一下,告诉编译器这是一个类。
而解决了上面这个问题以外,我们再运行还是会编译报错,这是因为在++中我们计算桶的位置时,用_ht访问了_table,但是_table是HashTable中的私有成员,类外不能访问私有,所以用一个GetTable,也可以用友元,__HashTable_Iterator类要访问HashTable类的私有,那__HashTable_Iterator类就是HashTable的友元。
// 前置声明
template<class K, class T, class KeyOfT, class Hash>
class HashTable;template<class K, class T, class KeyOfT, class Hash>
struct __HashTable_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HashTable_Iterator(Node* node, HT* ht):_node(node), _ht(ht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self& operator++(){// 当前桶没走完if (_node->_next){_node = _node->_next;}// 当前桶走完了else{KeyOfT kot;Hash hs;// 计算当前是几号桶size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();// 从下一个桶开始hashi++;while (hashi < _ht->_table.size()){if (_ht->_table[hashi]){_node = _ht->_table[hashi];break;}else{hashi++;}}if (hashi == _ht->_table.size()){_node = nullptr;}}return *this;}Self operator++(int){Self tmp = *this;++*this;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{// 友元template<class K, class T, class KeyOfT, class Hash>friend struct __HashTable_Iterator;typedef HashNode<T> Node;
public:typedef __HashTable_Iterator<K, T, KeyOfT, Hash> iterator;
};
实现好了内部细节,接下来来实现begin和end以及给上层套一层壳
template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{// 友元template<class K, class T, class KeyOfT, class Hash>friend struct __HashTable_Iterator;typedef HashNode<T> Node;
public:typedef __HashTable_Iterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (int i = 0; i < _table.size(); i++){if (_table[i]){return iterator(_table[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}
};
namespace hx
{template<class K>class unordered_set{//...public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}
namespace hx
{template<class K, class V>class unordered_map{//...public:typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}private:hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;};
}
2.3 const迭代器
const迭代器和普通迭代器只有operator*和operator->的返回值不一样,所以我们再加上两个模版参数来控制即可。
template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct __HashTable_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HashTable_Iterator(Node* node, HT* ht):_node(node), _ht(ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){//...}Self operator++(int){//...}bool operator!=(const Self& s){//...}bool operator==(const Self& s){//...}
};template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{// 友元template<class K, class T, class Pef, class Ptr, class KeyOfT, class Hash>friend struct __HashTable_Iterator;typedef HashNode<T> Node;
public:typedef __HashTable_Iterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef __HashTable_Iterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){for (int i = 0; i < _table.size(); i++){if (_table[i]){return iterator(_table[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{for (int i = 0; i < _table.size(); i++){if (_table[i]){return const_iterator(_table[i], this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}
};
namespace hx
{template<class K>class unordered_set{//...public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}
namespace hx
{template<class K, class V>class unordered_map{//...public:typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}private:hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;};
}
当我们测试的时候,又会出编译报错
这个地方提示构造函数调不到,这是为什么呢?我们来分析一下,this指针是类类型对象的指针,这个end是个const成员函数,const修饰的是this,this的类型是const HashTable* const this,而在迭代器中,_ht是没有const修饰的,所以这是一个权限放大,传不上去,我们在迭代器中把_ht设计成const的就可以了。
template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct __HashTable_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;Node* _node;const HT* _ht;__HashTable_Iterator(Node* node, const HT* ht):_node(node), _ht(ht){}
};
2.4 解决Key不能修改的问题
就在要在结点中存的参数前加上const修饰,注意:unordered_map中不要加在pair前,因为value是可以修改的,所以加在Key前就可以。
namespace hx
{template<class K>class unordered_setpublic:typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT>::const_iterator const_iterator;private:hash_bucket::HashTable<K, const K, SetKeyOfT> _ht;};
}
namespace hx
{template<class K, class V>class unordered_map{public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;};
}
2.5 unordered_map的[]实现
要实现[]首先要把insert的返回值改成pair<iterator, bool>,而insert里又要先调用find,所以我们先把find的返回值改成迭代器
iterator find(const K& key)
{KeyOfT kot;Hash hs;size_t hashi = hs(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return iterator(nullptr, this);
}
pair<iterator, bool> insert(const T& data)
{KeyOfT kot;Hash hs;iterator it = find(kot(data));if (it._node)return { it, false };// 扩容if (_n / _table.size() == 1){//...}size_t hashi = hs(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return{ iterator(newnode, this), true };
}
V& operator[](const K& key)
{pair<iterator, bool> ret = insert({ key, V() });return ret.first->second;
}
[]的实现是先去调用insert,插入一个value的匿名对象就可以,会去调用value的默认构造,并且接收返回值,ret.first拿到的就是迭代器对象,里面存储的就是节点的指针,不管这个key是之前就存在,还是新插入进去的,都是这个节点的指针,迭代器对象去调用operator->拿到pair的地址,再解引用拿second,为了可读性两个->省略成了一个。
注:那到了这里我们的模拟实现可以说是已经没有什么问题,都可以跑通了,但是还有一个问题,就是HashFunc这个缺省参数,对于使用的人来说,并不知道底层哈希表的结构,如果我想自己控制的时候,没有办法传进去,所以我们需要在上层再增加一个模版参数,在上层传进来,把缺省参数的位置向外移了一层。其次就是上层的find和erase,直接套层壳就好了,这两点我们在这里不单独看代码了,在最后的源码中会写。
三、unordered_set/unordered_map以及哈希表源码
3.1 HashTable.h
#include<vector>static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291
};inline unsigned long __stl_next_prime(unsigned long n)
{const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last ? *(last - 1) : *pos;
}template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};// 特化
template<>
struct HashFunc<string>
{size_t operator()(const string& str){size_t sum = 0;for (auto ch : str){sum *= 131;sum += ch;}return sum;}
};namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}};// 前置声明template<class K, class T, class KeyOfT, class Hash>class HashTable;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>struct __HashTable_Iterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashTable_Iterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;Node* _node;const HT* _ht;__HashTable_Iterator(Node* node, const HT* ht):_node(node), _ht(ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){// 当前桶没走完if (_node->_next){_node = _node->_next;}// 当前桶走完了else{KeyOfT kot;Hash hs;// 计算当前是几号桶size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();// 从下一个桶开始hashi++;while (hashi < _ht->_table.size()){if (_ht->_table[hashi]){_node = _ht->_table[hashi];break;}else{hashi++;}}if (hashi == _ht->_table.size()){_node = nullptr;}}return *this;}Self operator++(int){Self tmp = *this;++*this;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}};template<class K, class T, class KeyOfT, class Hash>class HashTable{// 友元template<class K, class T, class Pef, class Ptr, class KeyOfT, class Hash>friend struct __HashTable_Iterator;typedef HashNode<T> Node;public:typedef __HashTable_Iterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef __HashTable_Iterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){for (int i = 0; i < _table.size(); i++){if (_table[i]){return iterator(_table[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{for (int i = 0; i < _table.size(); i++){if (_table[i]){return const_iterator(_table[i], this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}HashTable(size_t size = __stl_next_prime(0)){_table.resize(size, nullptr);}// 析构~HashTable(){for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}// 拷贝构造,调用插入,一个一个插入就可以HashTable(const HashTable<K, T, KeyOfT, Hash>& ht){_table.resize(ht._table.size(), nullptr);for (int i = 0; i < ht._table.size(); i++){Node* cur = ht._table[i];while (cur){insert(cur->_kv);cur = cur->_next;}}}// hb2 = hb1// 赋值,现代写法,调用拷贝构造神拷贝HashTable<K, T, KeyOfT, Hash>& operator=(HashTable<K, T, KeyOfT, Hash> ht){swap(_table, ht._table);swap(_n, ht._n);return *this;}pair<iterator, bool> insert(const T& data){KeyOfT kot;Hash hs;iterator it = find(kot(data));if (it._node)return { it, false };// 扩容if (_n / _table.size() == 1){vector<Node*> newtable(__stl_next_prime(_table.size() + 1), nullptr);for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newtable.size();cur->_next = newtable[hashi];newtable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newtable);}size_t hashi = hs(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return{ iterator(newnode, this), true };}iterator find(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return iterator(nullptr, this);}bool erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _table.size();Node* cur = _table[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (prev == nullptr){_table[hashi] = cur->_next;}else{prev->_next = cur->_next;}--_n;delete cur;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _table;size_t _n = 0;};
}
3.2 unordered_set
namespace hx
{template<class K, class Hash = HashFunc<K>>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator, bool> insert(const K& key){return _ht.insert(key);}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}private:hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;};
}
3.3 unordered_map
namespace hx
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = insert({ key, V() });return ret.first->second;}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};
}
总结
在封装哈希表的时候,大的思路和方法和封装set/map时是一样的,可能在迭代器这块哈希表还简单一些,但是其他的细节,比如前置声明、友元、权限放大这些细节,还是需要细心才能发现,而且在这里这个类型非常长,很多模版又会互相牵扯,所以报错就非常多,非常长,大家还是一定要能耐心思考,发现问题,那本篇文章到这里就结束了,如果大家觉得小编写的还不错,可以给一个三连表示支持,谢谢大家!!!