开始之前,先上unordered_map的定义:
template<class Key,
class Ty,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, Ty> > >
class unordered_map;
> class unordered_map
当我们在使用自定义类型作为unordered_map的key的时候,要求我们的自定义类型满足一下两点:
1. 提供hash函数
这是由于unordered_map的底层数据结构是hash table,所以需要一个hash函数来将key序列化然后返回哈希值
class Dylan
{
public:
Dylan() { name = ""; addr = ""; }
Dylan(std::string na, std::string ad) : name(na), addr(ad) {};
const string getName() const { return name; }
const string getAddr() const { return addr; }
private:
std::string name;
std::string addr;
int age = 0;
};
namespace std //用自定义类型特例化一个hash模板,间接调用原生hash
{
template<>
struct hash<Dylan>
{
size_t operator() (const Dylan& s) const noexcept
{
string hasStr = s.getName() + "+" + s.getAddr();
return std::hash<std::string>(hasStr); // 调用原生hash.
}
};
}
int main()
{
unordered_map<Dylan, int> map;
Dylan dy1("111","222");
Dylan dy2("222","333");
map.insert({ dy1,1 });
map.insert({ dy2,2 });
cout << map[dy1] << " " << map[dy2] << endl;
system("pause");
return 0;
}
unordered_map 的默认hash函数是一个名为hash的模板,template<> hash{},当然我们也可以自定义一个hash函数,然后在声明map的时候把这个自定义函数的名字传进来,就像下面这样:
class Dylan{...}
struct myOwnHash
{
size_t operator() (const Dylan& s) const noexcept
{
string hasStr = s.getName() + "+" + s.getAddr();
return std::hash<std::string>(hasStr);
}
};
int main()
{
unordered_map<Dylan, int, myOwnHash> map;
....
}
2. 提供eqaul_to函数
当我们编译时,发现还会遇到如下error:
error C2676: binary '==': 'const _Ty' does not define this operator
or a conversion to a type acceptable to the predefined operator
message : see reference to function template instantiation 'bool std::equal_to<_Kty>::operator ()(const _Ty &,const _Ty &) const' being compiled
这是因为,unordered_map 还需要一个可以比较两个key是否相等的函数,实际上unordered_map会提供一个缺省的equal_To函数,这是个模板函数,他会以我们自定义的类型来实例化,然后调用自定义类型(即Dylan)的==运算符,那么消除上面这个error的办法也就显而易见了,依然有两种方法。方法一是在类定义中,重载==运算符
class Dylan
{
public:
Dylan() { name = ""; addr = ""; }
Dylan(std::string na, std::string ad) : name(na), addr(ad) {};
const string getName() const { return name; }
const string getAddr() const { return addr; }
bool operator==(const Dylan& dy1) const { return this->getName() == dy1.getName(); }
//切记,将这个函数声明为const类型,否则会遇到编译错误
private:
std::string name;
std::string addr;
int age = 0;
};
int main()
{
unordered_map<Dylan, int> map; //这样就可以像往常一样使用unordered_map了
Dylan dy1("111","222");
Dylan dy2("222","333");
map.insert({ dy1,1 });
map.insert({ dy2,2 });
cout << map[dy1] << " " << map[dy2] << endl;
system("pause");
return 0;
}
方法二是自定义自己的equalto函数,让后将该函数名字放入声明的第四个参数中
class Dylan{...}
struct myOwnHash
{
size_t operator() (const Dylan& s) const noexcept
{
std::hash<string> myhash;
string hasStr = s.getName() + "+" + s.getAddr();
return myhash(hasStr);
}
};
struct myOwnEqualTo
{
size_t operator() (const Dylan& s, const Dylan& s1) const noexcept
{
return (s.getName() == s1.getName());
}
};
int main()
{
unordered_map<Dylan,int, myOwnHash,myOwnEqualTo> map;
....
}