C++中如何实现含继承对象的无序容器?
解决方案
不需要拆分容器,以下是几种符合C++惯用实践的解决思路:
方案1:基类定义纯虚相等判断接口,派生类实现
在抽象基类players中声明纯虚的相等判断方法,让派生类自行实现具体逻辑,再给无序容器传入自定义的相等谓词:
#include <memory> #include <unordered_set> #include <string> class players { public: virtual ~players() = default; // 纯虚相等判断接口 virtual bool equals(const players& other) const = 0; const std::string& get_name() const { return name; } protected: std::string name; }; class attackers : public players { public: attackers(std::string n) { name = std::move(n); } bool equals(const players& other) const override { // 先判断类型是否匹配,再比较字段 if (const auto* other_attacker = dynamic_cast<const attackers*>(&other)) { return name == other_attacker->name; } return false; } }; class defenders : public players { public: defenders(std::string n) { name = std::move(n); } bool equals(const players& other) const override { if (const auto* other_defender = dynamic_cast<const defenders*>(&other)) { return name == other_defender->name; } return false; } }; // 自定义相等谓词,调用对象的equals方法 struct PlayerEqual { bool operator()(const std::shared_ptr<players>& a, const std::shared_ptr<players>& b) const { if (!a || !b) return a == b; // 处理空指针情况 return a->equals(*b); } }; // 基于name的哈希函数 struct PlayerHash { size_t operator()(const std::shared_ptr<players>& p) const { if (!p) return 0; return std::hash<std::string>()(p->get_name()); } }; // 最终的无序集合类型 using AllPlayersSet = std::unordered_set<std::shared_ptr<players>, PlayerHash, PlayerEqual>;
这种方式遵循多态设计原则,把相等判断的具体逻辑交给派生类,完全避免了继承operator==带来的切片、逻辑不一致等问题。
方案2:直接在自定义相等谓词中完成类型检查与字段对比
如果不想修改基类代码,可以直接在相等谓词里做类型判断和字段对比,不需要基类提供虚函数:
struct PlayerEqual { bool operator()(const std::shared_ptr<players>& a, const std::shared_ptr<players>& b) const { if (!a || !b) return a == b; // 先判断两个对象类型是否相同 if (typeid(*a) != typeid(*b)) return false; // 再比较name字段(依赖基类提供name的访问接口) return a->get_name() == b->get_name(); } }; // 哈希函数同方案1 struct PlayerHash { size_t operator()(const std::shared_ptr<players>& p) const { if (!p) return 0; return std::hash<std::string>()(p->get_name()); } }; using AllPlayersSet = std::unordered_set<std::shared_ptr<players>, PlayerHash, PlayerEqual>;
这种方式更轻量化,适合无法修改基类的场景,但依赖RTTI(运行时类型信息),大多数编译器默认开启该功能。
关键注意点
- 哈希函数与相等谓词必须逻辑一致:如果哈希基于
name生成,那么被相等谓词判定为相等的两个对象,name必须完全相同,否则会出现哈希冲突无法正确处理的问题。 - 如果业务逻辑允许只要name相同就判定为相等(忽略类型),可以去掉类型检查的逻辑,直接比较
name即可。
内容的提问来源于stack exchange,提问作者julian
相关产品推荐
相关产品推荐

