unordered_map按内存地址识别对象时at函数异常,如何正确校验身份
C++ unordered_map按对象身份识别key的实现方案
问题根因
你给出的代码调用at()抛出异常的核心原因是:std::unordered_map存储key时会生成原对象的拷贝副本,你自定义的operator==是直接比较两个对象的内存地址,插入map的是data1的拷贝对象,和后续传入at()的原data1地址不一致,匹配失败就触发了std::out_of_range异常。
正确实现方式
方案1:直接使用对象指针作为key(最推荐)
指针本身就是内存地址,标准库默认支持指针的哈希与比较逻辑,不需要额外自定义运算符,实现最简单性能最高:
#include <iostream> #include <unordered_map> class Data {}; int main() { // 直接以Data*作为key,天然按地址识别身份 std::unordered_map<Data*, Data> map; Data data1; Data data2; map[&data1] = data2; map.at(&data1); // 正常匹配,无异常 return 0; }
方案2:自定义哈希函数映射对象地址
如果你坚持要用对象本身作为key,需要修改哈希函数返回对象地址的哈希值,同时插入时要避免key的额外拷贝:
#include <iostream> #include <unordered_map> #include <cstdint> class Data { public: bool operator==(const Data& other) const { return this == &other; } struct DataHasher { std::size_t operator()(const Data& other) const { // 将对象地址转为整数生成哈希值 return std::hash<uintptr_t>()(reinterpret_cast<uintptr_t>(&other)); } }; }; int main() { std::unordered_map<Data, Data, Data::DataHasher> map; Data data1; Data data2; // 用emplace原地构造key,避免产生新的拷贝对象 map.emplace(std::piecewise_construct, std::forward_as_tuple(data1), std::forward_as_tuple(data2)); map.at(data1); // 匹配成功 return 0; }
注意:该方案存在局限性,如果对象地址发生变化(比如被移动、销毁重建)会导致匹配失败,仅适合对象生命周期固定、地址不变的场景
方案3:给对象绑定唯一身份ID
如果你的对象存在拷贝、移动需求,又需要保持身份识别的稳定性,可以给每个对象分配全局唯一ID,比较和哈希都基于该ID实现,不受地址变化影响:
#include <iostream> #include <unordered_map> #include <atomic> class Data { private: // 全局ID生成器,保证每个对象ID唯一 static inline std::atomic<uint64_t> s_id_counter = 0; const uint64_t m_obj_id = s_id_counter++; public: bool operator==(const Data& other) const { return m_obj_id == other.m_obj_id; } struct DataHasher { std::size_t operator()(const Data& other) const { return std::hash<uint64_t>()(other.m_obj_id); } }; }; int main() { std::unordered_map<Data, Data, Data::DataHasher> map; Data data1; Data data2; map[data1] = data2; map.at(data1); // 正常匹配 return 0; }
内容的提问来源于stack exchange,提问作者Ronnie Marksch
相关产品推荐
相关产品推荐

