【问题标题】:Vector going out of bounds矢量超出范围
【发布时间】:2018-12-07 17:28:31
【问题描述】:

我正在尝试遍历包含 6 个“国际象棋”棋子的列表。每一轮他们移动一个随机数量,如果他们降落在另一轮上,他们就会“杀死”它。

问题是当我的向量中的最后一个片段杀死另一个片段时,我得到一个向量“超出范围”错误。我猜这是因为我在遍历向量的同时还从中删除了项目,但是当我擦除一块时我并没有增加计数,所以我不完全确定。任何帮助将不胜感激。

这是我的向量:

vector<Piece*> pieces;
pieces.push_back(&b);
pieces.push_back(&r);
pieces.push_back(&q);
pieces.push_back(&b2);
pieces.push_back(&r2);
pieces.push_back(&q2);

这是我使用的循环:

while (pieces.size() > 1) {
    cout << "-------------- Round " << round << " --------------" << endl;
    round++;
    cout << pieces.size() << " pieces left" << endl;
    i = 0;
    while (i < pieces.size()) {
        pieces.at(i)->move(board.getMaxLength());
        j = 0;
        while (j < pieces.size()) {
            if (pieces.at(i) != pieces.at(j) && col.detectCollision(pieces.at(i), pieces.at(j))) {
                cout << pieces.at(i)->getName() << " has slain " << pieces.at(j)->getName() << endl << endl;
                pieces.at(i)->setKills(pieces.at(i)->getKills() + 1);
                pieces.erase(pieces.begin() + j);
            }
            else {
                j++;
            }
        }
        i++;
    }
}

解决方案

pieces.erase(pieces.begin() + j);
break;

【问题讨论】:

  • 这就是调试器的用途。使用您的调试器,观察ij 的值,以及向量的大小如何变化,原因以及错误应该很明显。了解如何使用调试器是每个 C++ 开发人员必备的技能。没有例外。附:您甚至可能在关键的at() 爆炸之前看到该错误。观察一下,就像魔术一样,在外循环的下一次迭代中,pieces.at(i) 与循环的上一次迭代不同,并问自己:这是怎么发生的?
  • 检查 this 以便在迭代向量时安全地从向量中删除项目
  • 你在设计这个程序时没有考虑到某个位置是什么?如果是这样,您为什么需要搜索需要“杀死”的部分?换句话说,为什么需要第二个while 循环?
  • @PaulMcKenzie 检查我认为的所有配对
  • 整个碰撞逻辑非常混乱。是什么让 j 项目被删除,而不是 i 项目?为什么i 项比j“更强”?我认为这就是麻烦开始的地方,而且没有一套连贯的规则。

标签: c++ vector


【解决方案1】:

你的逻辑需要一点改进。

按照您的编码方式,国际象棋的“回合制”性质似乎已被一种“优先级列表”所取代——靠近向量开头的棋子可以先移动,因此,优先粉碎其他碎片。

我不知道你是否希望这个逻辑是对还是错。无论如何,麻烦似乎是由于无条件执行该行

i++;

如果你移除一个片段,它不应该被执行,同样的原因 'j++' 没有被执行:你会跳过一个片段。

【讨论】:

    【解决方案2】:

    我有一种很强烈的感觉,就是这行代码:

    i++;
    

    是您的罪魁祸首,它缺少所需的中断条件或循环中缺少的另一个条件检查。因为它与嵌套 while 循环的条件有关,因为它们基于向量的当前大小并且不会相应地更新。

    while (pieces.size() > 1) {
        // ...
        while (i < pieces.size()) {
            // ...
            while (j < pieces.size()) {
                  // ...
            }
        }
    } 
    

    这是因为您在最内部的嵌套循环中调用它:

     pieces.erase(pieces.begin() + j);
    

    您在嵌套的 while 循环内,如果满足某个条件,您将在向量内的此索引位置擦除对象,而您仍在内部 while 循环内,您永远不会中断或检查以查看如果索引仍然有效。

    最初,您使用具有 6 个条目的向量进入此 while 循环,然后在嵌套循环中对其调用擦除,现在您的向量有 5 个条目。

    这会对您的循环造成严重破坏,因为您的索引计数器 ij 是根据向量的原始长度设置的,大小为 6,但现在向量已减小到5 当您仍在最内部的嵌套循环中时,您永远不会中断,也不会检查索引是否有效。在下一次迭代中,这些值现在无效,因为您永远不会跳出循环来根据向量的新大小重置索引,也不会检查它们是否有效。


    尝试运行这个简单的程序,它将演示我所说的嵌套循环中的索引无效。

    int main() {
        std::vector<std::string> words{ "please", "erase", "me" };
    
        std::cout << "Original Size: " << words.size() << '\n';
        for (auto& s : words)
            std::cout << s << " ";
        std::cout << '\n';
    
        words.erase(words.begin() + 2);
    
        std::cout << "New Size: " << words.size() << '\n';
        for (auto& s : words)
            std::cout << s << " ";
        std::cout << '\n';
    
        return 0;
    }
    

    -输出-

    Original Size: 3
    please erase me
    New Size: 2
    please erase
    

    【讨论】:

    • 您的代码非常有用,谢谢。你需要一个断点是对的,我在几个不同的地方尝试了它们,但是在 pieces.erase(pieces.begin() + j); 之后行是它工作的地方。
    【解决方案3】:

    你应该在本地保存pieces.at(i),并在你使用pieces.at(i)的任何地方使用这个局部变量。

    为了避免元素越界和逻辑问题,您可以使用std::list

    顺便说一句,你应该使用std::vector&lt;Piece*&gt;,只有当它们是非拥有的指针,否则你应该使用智能指针,可能是unique_ptr

    【讨论】:

    • 如果 j 从 i+1 开始,那么碰撞检测不会忽略向量中它之前的所有部分吗?
    • @bobnog 是的,但是如果detectCollision(a,b) 返回与detectCollision(b,a) 相同的结果,那么这并不重要,因为这些对已经被考虑过了。
    • 我现在尝试用 j = i+1 运行代码,它没有出错,但它似乎忽略了当 queen2 移动时的碰撞检测
    • @bobnog 尝试第一个选项。 auto cur = pieces.at(i)。并使用这个局部变量。
    • @bobnog yeh,我错过了这一行 pieces.at(i)-&gt;move(board.getMaxLength());。第二个选项在这里不是一个选项。很抱歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2020-03-14
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    相关资源
    最近更新 更多