我有一张地图,用于保持球队名称和球队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&#39;中的球员。和&#39; TeamB&#39;是相同但仍然在集合中添加两个指针,我无法弄清楚为什么..有什么我缺少的东西?
答案 0 :(得分:2)
你的集合包含指针,而不是Player
个对象。这意味着指针比较用于确定等价。
当然,TeamA中第一个对象的地址与TeamB中第一个玩家的地址不同,即使它们包含相同的数据。
如果您想通过指针对象进行比较,请使用自定义比较器类型:
PlayerComp {
bool operator()(Player* lhs, Player* rhs){
return *lhs < *rhs;
}
};
std::set<Player*, PlayerComp> players;
而玩家类在其自己的实例上实现operator<
,而不是在指针上实现:
bool operator<(const Player& rhs) const
{
return wins < rhs.wins;
}
答案 1 :(得分:1)
如果我的假设是正确的,这将不会像你期望的那样成功!
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);
但这也不会有效:您的比较仅基于胜利!如果你输入两个不同的玩家(顺便说一句,如果你应用了StoryTeller的修复!),两者都以0的胜利开始。所以两个玩家将比较相等(a < b
和b < a
这两个都不适用于平等),所以只有一个玩家才会进入该组......
此外,std::set
没有“自动更新”功能!它要求其条目保持不变(至少其成员用于比较!)。因此,如果您在比赛的运行中更新您的胜利,您的集合中的排序将完全丢失(除此之外,修改地图的键或设置实际上是未定义的行为)。
结论: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());
嘿,这只是来自StoryTeller答案的PlayerComp
......或者我们改用lambda:
std::sort
(
players.begin(), players.end(),
[](Player const* x, Player const* y) { return *x < *y; }
);
最后,不要忘记再次删除播放器对象:
for(auto p : players)
{
delete p;
}
如果使用智能指针,可以跳过删除,例如: G。像这样:
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; }
);