自定义哈希函数类型
以往在定义哈希的时候都是用两个参数模板,但是今天遇到的三个参数模板的,这里扩展一下。
在 C++ 中,std::unordered_map
的第三个模板参数是哈希函数类型,用于计算键(Key)的哈希值。你看到的 PageIdHash
就是专门为 PageId
类型定制的哈希函数,这是处理自定义类型作为键时的常见做法。
一、std::unordered_map
的模板参数
unordered_map
的完整模板定义如下:
template<
class Key, // 键类型
class T, // 值类型
class Hash = std::hash<Key>, // 哈希函数类型(默认使用 std::hash)
class KeyEqual = std::equal_to<Key>,// 键比较函数类型(默认使用 std::equal_to)
class Allocator = std::allocator< std::pair<const Key, T> >// 内存分配器(很少修改)
> class unordered_map;
关键参数说明:
Key
:键的类型(如PageId
)。T
:值的类型(如frame_id_t
)。Hash
:计算键哈希值的函数类型,默认是std::hash<Key>
。
二、为什么需要自定义哈希函数?
当键类型是自定义类型(如 PageId
类)时,C++ 标准库不知道如何计算其哈希值,因此需要手动提供:
PageId
是自定义类型,没有默认的哈希函数。PageIdHash
是专门为PageId
定制的哈希函数类型。
三、PageIdHash
的实现示例
struct PageId {
int file_id; // 文件ID
int page_num; // 页号
// 判断两个 PageId 是否相等(必须重载)
bool operator==(const PageId& other) const {
return file_id == other.file_id && page_num == other.page_num;
}
};
// 自定义哈希函数
struct PageIdHash {
// 计算 PageId 的哈希值
std::size_t operator()(const PageId& p) const {
// 使用标准库的哈希函数组合多个成员
return std::hash<int>()(p.file_id) ^ (std::hash<int>()(p.page_num) << 1);
}
};
// 使用自定义哈希函数的 unordered_map
std::unordered_map<PageId, frame_id_t, PageIdHash> page_table_;
四、为什么 std::string
不需要自定义哈希?
- 内置类型(如
int
、std::string
)已有默认的std::hash
实现。 - 示例:
// 无需第三个参数,因为 std::string 有默认哈希 std::unordered_map<std::string, int> name_to_age;
五、总结
-
三个模板参数的含义:
PageId
:键类型(数据页ID)。frame_id_t
:值类型(帧号)。PageIdHash
:自定义哈希函数,用于计算PageId
的哈希值。
-
何时需要第三个参数?
- 当键类型是自定义类型(如类、结构体)时,必须自定义哈希函数。
- 当键类型是内置类型(如
int
、std::string
)时,可省略第三个参数。
-
配套要求:
- 自定义哈希函数时,通常还需要为键类型重载
operator==
(用于判断两个键是否相等)。
- 自定义哈希函数时,通常还需要为键类型重载