如何从使用for循环中的向量大小的向量中删除元素

时间:2020-07-01 10:47:03

标签: c++ vector

我有一个游戏,我向目标射击子弹,然后删除被子弹击中的物体以及不在屏幕上的子弹。

例如:

std::vector<object> object_list;
for(size_t i = 0; i < object_list.size(); i++)
{

    if(object_list[i].hit())
    {
        object_list.erase(object_list.begin() + i);
    }
    else
        object_list[i].draw();
}

此问题是,当我删除对象时,向量的大小会减小,因此当检查条件时,向量会失败,并且会出现诸如“向量下标超出范围”之类的错误。我可以选择不渲染那些尚未被击中的小行星来渲染小行星,但是问题是没有。命中(分裂)时对象的数量增加,因此最终程序将变慢。对于屏幕外的项目符号,我使用了类似的概念,但是找不到解决方法。我正在寻找一种解决方案,以这种或更好的方式删除元素。

对象和项目符号都是类。

7 个答案:

答案 0 :(得分:5)

您应该将循环分为两部分:

  1. 删除所有“匹配”元素:
const { Parser } = require('json2csv');
 
const fields = ['field1', 'field2', 'field3'];
const opts = { fields };
 
try {
  const parser = new Parser(opts);
  const csv = parser.parse(myData);
  console.log(csv);
} catch (err) {
  console.error(err);
}
  1. 剩余现金:
object_list.erase(std::remove_if(object_list.begin(), 
object_list.end(), [](auto&& item) { return item.hit(); }),
object_list.end());

它更安全,更易读。

答案 1 :(得分:2)

与其他答案具有相同的想法,但是使用迭代器可以使此代码更容易

for (auto i = object_list.begin(); i != object_list.end(); )
{
    if (i->hit())
    {
        i = object_list.erase(i);
    }
    else
    {
        i->draw();
        ++i;
    }
}

vector::erase将迭代器返回到下一个元素,您可以使用该元素继续循环。

答案 2 :(得分:2)

使用range-v3库(C ++ 20)的功能方法

[...]我正在寻找这种或删除元素的更好方法的解决方案。

使用ranges::actions::remove_if库中的range-v3操作,您可以使用功能性编程风格的方法来原位更改object_list容器:

object_list |= ranges::actions::remove_if(
    [](const auto& obj) { return obj.hit(); });

随后是随后的ranges:for_each调用以绘制对象:

ranges::for_each(object_list, [](const auto& obj){ obj.draw(); });

DEMO

答案 3 :(得分:1)

您可以执行以下操作:

for (size_t i = 0; i < object_list.size(); )
{
    if (object_list[i].hit())
        object_list.erase(object_list.begin() + i)
    else
    {
        object_list[i].draw()
        ++i;
    }
}

答案 4 :(得分:0)

让我们说您在i = 5处并且该对象已被命中,删除该元素后,i = 6处的obj移至i = 5,并且您尚未检查它,因此只需添加{{ 1}}。

另一种方法是-

i--;

此外,从矢量中删除对象可能会更快,在该矢量中执行将对象标记为命中的代码,这样,您只需要绘制列表中遗漏的所有对象即可。了解更多有关如何执行所有这些操作的背景将有助于确定更合适的特定方法:)

答案 5 :(得分:0)

显示的代码不会失败或给向量下标超出范围-它只是不考虑每个对象,因为它会在删除对象后跳过该元素。

有关采用C ++ 11及更高版本的概念的非常简短的解决方案,请参见answer by Equodthe one by dfri

为更好地理解问题,和/或如果您必须坚持使用索引进行for循环,则基本上有两种选择:

  1. 以相反的方向遍历向量(即从最后一个元素开始),然后当前要移动的 之后的项目就没问题了;
for (int i=object_list.size()-1; i>=0; --i)
{
    if (object_list[i].hit())
    {
        object_list.erase(object_list.begin() + i)
    }
    else
    {
        object_list[i].draw()
    }
}
  1. 或者,如果顺序很重要(如我想像的那样绘制物品),并且您必须从前到后进行迭代,那么仅增加计数器 i not 删除了当前元素:
for (int i=0; i<object_list.size(); /* No increase here... */ )
{
    if (object_list[i].hit())
    {
        object_list.erase(object_list.begin() + i);
    }
    else
    {
        object_list[i].draw();
        ++i;   // ...just here if we didn't remove the element
    }
}

答案 6 :(得分:0)

我怀疑std::vector不是您想要的容器(但是,我当然不知道整个代码)。每次擦除操作都意味着重新分配了向量的右部分(然后是对象的副本),这可能会非常昂贵。而您的实际问题是设计问题的症状。

从我看来,std::list可能更好:

std::list<object> objects;
// ...
for(std::list<object>::iterator it = objects.begin(); it != objects.end();)
{
    if(it->hit())
      objects.erase(it++); // No object copied
    else
    {
      it->draw();
      ++it;
    }
}