代码之家  ›  专栏  ›  技术社区  ›  Стоил Янков

std::set不检测重复的自定义对象

  •  0
  • Стоил Янков  · 技术社区  · 7 年前

    我有一张保留球队名称和球员的地图 std:pair<std::string, std::vector<std::string> >

        class Player
    {
    public:
        int wins;
        std::string name;
    
        Player() {}
        Player(std::string name) : wins(0), name(name) {}
    
        bool operator<(const Player* rhs) const
        {
            return this->wins > rhs->wins;
        }
    
        bool operator()(const Player* lhs, const Player* rhs) const
        {
            return lhs->wins > rhs->wins;
        }
    };
    
    int main()
    {
        // EXAMPLE
        std::set<Player*> players;
        std::map<std::string, std::vector<Player> > teams;
    
        teams["TeamA"].push_back(Player("a"));
        teams["TeamB"].push_back(Player("a"));;
        players.insert(&teams["TeamA"].front());
        players.insert(&teams["TeamB"].front());
    
        std::cout << players.size(); // output 2 
    
        return 0;
    }
    

    正如你所看到的,“TeamA”和“TeamB”中的球员是相同的,但仍然在集合中添加了两个指针,我不明白为什么……我缺少了什么吗?

    2 回复  |  直到 7 年前
        1
  •  2
  •   StoryTeller - Unslander Monica    7 年前

    您的集合包含指针,而不是 Player 物体。这意味着

    当然,TeamA中第一个对象的地址与TeamB中第一个玩家的地址不同,即使它们包含相同的数据。

    如果要按指针对象进行比较,请使用自定义比较器类型:

    PlayerComp {
      bool operator()(Player* lhs, Player* rhs){
        return *lhs < *rhs;
      }
    };
    
    std::set<Player*, PlayerComp> players;
    

    而player类实现 operator< 在其自身实例上,而不是指针上:

    bool operator<(const Player& rhs) const
    {
        return wins < rhs.wins;
    }
    
        2
  •  1
  •   Aconcagua    7 年前

    如果我的假设是正确的,这不会像你预期的那样成功!

    teams["TeamA"].push_back(Player("a"));
    teams["TeamB"].push_back(Player("a"));
    

    我假设你想让一个叫“a”的球员成为两队的一员。如果该玩家在团队A中获胜,则其wins属性将增加,与在团队B中获胜的情况相同。

    然而,你实际上做的是创建两个不同的玩家,都被称为“a”,他们都出现在集合中(好吧,实际上不是,见下文…),每个人都保持自己的胜利(第一个玩家“a”是a队的胜利,第二个是B队的胜利)。

    你怎么了

    std::set<Player> players;
    std::map<std::string, std::vector<Player*> > teams;
    auto entry = players.insert(Player("a"));
    teams["TeamA"].push_back(&entry->first);
    

    但这也行不通: a < b b < a

    此外, std::set 没有“自动更新”功能!它要求其条目保持不变(至少其成员用于比较!)。因此,如果你在比赛中更新你的胜利,你的集合中的排序将完全丢失(除此之外,修改地图或集合的关键点实际上是未定义的行为)。

    结论: 标准::设置

    // yes, a vector...
    // we'll need to maintain sorting ourselves...
    std::vector<Player*> players;
    // need pointers here, too, as pointers to objects
    // (those within the teams!) might INVALIDATE on
    // vector<Player> resizing!!!
    
    std::map<std::string, std::vector<Player*>> teams;
    
    players.push_back(new Player("a"));
    teams["TeamA"].push_back(players.back());
    teams["TeamB"].push_back(players.back());
    
    players.push_back(new Player("b"));
    teams["TeamA"].push_back(players.back());
    teams["TeamC"].push_back(players.back());
    

    现在让我们一个团队获胜:

    for(auto p : teams["TeamA"])
    {
        ++p->wins;
    }
    

    std::sort(players.begin(), players.end(), PlayerComp());
    

    嘿,这只是 PlayerComp 根据故事讲述者的回答……或者我们使用lambda代替:

    std::sort
    (
        players.begin(), players.end(),
        [](Player const* x, Player const* y) { return *x < *y; }
    );
    

    for(auto p : players)
    {
        delete p;
    }
    

    如果使用智能指针,您可以跳过删除,例如:

    std::vector<std::unique_ptr<Player>> players;
    
    players.push_back(std::make_unique<Player>("a"));
    teams["TeamA"].push_back(players.back().get());
    
    for(auto p : teams["TeamA"])
        ++p->score;
    std::sort
    (
        players.begin(), players.end(),
        [](std::unique_ptr<Player> const& x, std::unique_ptr<Player> const& y) { return *x < *y }
    );
    

    std::shared_ptr ,这避免了悬空指针的可能性(价格是创建、分配和销毁过程中的一些对象管理开销,而不是在访问智能指针时):

    std::vector<std::shared_ptr<Player>> players;
    std::map<std::string, std::vector<std::shared_ptr<Player>>> teams;
    
    players.push_back(std::make_shared<Player>("a"));
    teams["TeamA"].push_back(players.back());
    
    
    for(auto p : teams["TeamA"])
        ++p->score;
    std::sort
    (
            players.begin(), players.end(),
            [](std::shared_ptr<Player> const& x, std::shared_ptr<Player> const& y) { return *x < *y; }
    );