【问题标题】:How do I change where the pointer is pointing to through a return statement?如何通过 return 语句更改指针指向的位置?
【发布时间】:2021-07-22 10:10:51
【问题描述】:

我有一个包含 ID 字符串的 Player 对象。我试图通过遍历玩家列表来检查玩家是否已经存在。如果播放器已经存在,我想返回现有播放器,否则我将创建一个 Player 对象的新实例。

Player *playerExists(const std::string &playerId, const std::vector<Player>* players)
{
    for (Player existingPlayer : *players)
    {
        if (existingPlayer.playerId == playerId)
        {
            std::cout << "Player " << existingPlayer.name << " exists" << std::endl;
            return &existingPlayer;
        }
    }
    return new Player();
}

问题似乎出在 return 语句中。我不知道如何将特定对象返回给指针。这似乎是我发现不会出错的唯一方法(谈论 return 语句中的 & sing)。

Player* player = playerExists("SomeID", listOfPlayers);
listOfPlayers->push_back(*player);
delete player;

我对使用原始指针很陌生,所以我可能只是不明白这里的问题是什么。如果有人能解释我做错了什么,我将不胜感激。

【问题讨论】:

  • 你的问题不是很清楚。请注意,您的for 循环正在复制每个Player,并且返回的地址肯定是错误的。尝试循环引用,看看是否能解决问题。
  • 注意——整个代码可以是一个使用std::find_if的两行函数,不需要循环。作为副产品,这将消除for 循环中的错误,因为不会有for 循环。
  • 如果允许,请将const std::vector&lt;Player&gt;* players 替换为const std::vector&lt;Player&gt; &amp; players。对引用的限制将使您的代码更加安全。
  • 是的,使用for (auto player = (*existingPlayer).begin(); player != (*existingPlayer).end(); player++) 使用player-&gt;playerId,如果找到则返回playerstd::vector 的元素是连续的,所以可以使用迭代器(指针),找到后返回指针。
  • @DavidC.Rankin 您的示例使用的是迭代器,而不是指针。然后它将向调用者返回一个迭代器。向量迭代器不保证被实现为指针。

标签: c++


【解决方案1】:

这段代码有几个问题。

循环的每次迭代都在向量中制作Player本地副本,因此如果找到匹配项,将返回指向该副本的指针.但是当当前迭代完成时,那个 copy 被销毁了,所以你最终会返回一个 悬空指针。要解决这个问题,您的循环需要对向量中的每个 Player 进行 reference

for (Player &amp;existingPlayer : *players)

下一个问题是,如果找不到匹配项,则返回 newed Player。问题在于调用者无条件地deleteing 返回的Player*,无论它是否是newed。向量拥有它所持有的Player 对象,因此delete 使用其中一个对象是未定义的行为

如果没有找到匹配项,更好的设计选择是让playerExists() 返回nullptr。然后,调用者可以根据返回的是 Player 还是 nullptr 执行不同的操作,例如:

Player* playerExists(const std::string &playerId, const std::vector<Player>* players)
{
    for (Player &existingPlayer : *players)
    {
        if (existingPlayer.playerId == playerId)
        {
            std::cout << "Player " << existingPlayer.name << " exists" << std::endl;
            return &existingPlayer;
        }
    }
    return nullptr;
}
Player* player = playerExists("SomeID", listOfPlayers);
if (player) {
    // ID found, use player as needed...
    listOfPlayers->push_back(*player); // why re-add a player that is already in the list???
} else {
    // ID not found, do something else...
    listOfPlayers->push_back(Player{}); // why add a blank player???
}

【讨论】:

  • 简单得多。您将需要 const Player* playerExists(...)for (const Player &amp;existingPlayer : *players)(或只是 auto)来获得 const 的正确性。
【解决方案2】:

解决此问题的不同方法。最大的问题可能在这一行:

for (Player existingPlayer : *players)

它正在复制(我认为)收藏中已有的玩家。

当您执行return &amp;existingPlayer; ... 时,它会返回堆栈上的一个对象。坏事会发生。

你最好迭代集合并返回一个迭代器,恕我直言......也许......

std::vector<Player>::const_iterator playerExists(const std::string &playerId, const std::vector<Player>& players)
{
    std::vector<Player>::const_iterator it = players.begin();
    for (; it != players.end(); ++it)
    {
        if (it->playerId == playerId)
        {
            std::cout << "Player " << it->name << " exists" << std::endl;
            break;
        }
    }
    return it;
}

你可以在返回后随时测试

使用返回的迭代器,你必须对它做一些事情......

auto it = playerExists(playerId, players);
if (it == players.end())
{
   // not found
} else {
   // found
}

【讨论】:

  • 代码无法编译,因为it 在到达return 之前就超出了范围
  • @RemyLebeau 是的。将迭代器声明移出循环。
【解决方案3】:

你的问题在那里:

for (Player existingPlayer : *players)

for 循环的每次迭代中,existingPlayer 是播放器的 副本,它存储在 for 范围内的局部变量中 循环。您正在返回一个指向局部变量的指针,该变量在每次循环迭代后被销毁。

要避免复制,请使用 reference

for (Player &existingPlayer : *players)

这里,existingPlayer 是对真实玩家的参考。这就是你对函数原型中的const std::string 所做的。

我还有一些建议可以改进您的代码:

  • 避免使用原始 C 指针,C++ 为您提供了工具,以一种(更)不易出错的方式获得相同的功能,因此更安全。如果您不需要 NULL 正如我在您的代码中看到的那样,那么参考可能会更好。否则,您应该考虑使用智能指针(std::unique_ptrstd::shared_ptr。在您的情况下,我会通过引用而不是指针来传递 players

  • std::vector 可能不是最好的容器。 选择正确的容器可能很困难。 目前您正在遍历整个数组以通过其 id 找到玩家。您应该使用使用哈希表的std::unordered_map&lt;std::string,Player&gt;,并且可能会加快查找时间。 我也会使用std::a_container_class&lt;Player&gt; 的 typedef 来避免在您更改容器类型或稍后从头开始编写容器时重写大量代码。

  • 复制Player 有意义吗? 如果没有,请显式删除copy constructorcopy assignment operator,如果您要复制Player,编译器会抛出错误。 请注意,这是一种设计选择,我经常这样做,同一播放器的多个实例是正常的,具体取决于您的对象设计。

  • 我对@9​​87654338@ 持怀疑态度。如果没有玩家存在,为什么要返回新玩家?根据我个人的解释(可能完全错误),我会返回一个迭代器。不涉及指针,您可以“取消引用”迭代器以获取对找到的玩家的引用,并且通过返回等于players.end() 的迭代器来表示玩家不存在。但要注意一个很大的缺点,一定要在修改容器和使迭代器失效之前进行测试。

根据我自己的设计选择,我为您的函数制作了一个未经测试且较短的版本:

#include <unordered_map>
typedef std::unordered_map<std::string,Player> PlayerCollection;
/* ... */
PlayerCollection::const_iterator playerExists(const std::string &playerId, const PlayerCollection &players)
{
    return players.find(playerId);
}

请记住,我的建议是固执己见,并非绝对真理。 阅读其他有不同想法的答案。

【讨论】:

    猜你喜欢
    • 2011-06-23
    • 2018-09-08
    • 2020-03-12
    • 1970-01-01
    • 2018-06-08
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    相关资源
    最近更新 更多