You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:35:25