从向量中删除项目,而在C++ 11范围内'for'循环?

Edd*_*223 91 c++ for-loop vector c++11

我有一个IInventory*的向量,我正在使用C++ 11范围循环遍历列表,以便对每个进行处理.

在做了一些东西之后,我可能想要从列表中删除它并删除对象.我知道我可以随时调用delete指针来清理它,但是在范围for循环中,从矢量中删除它的正确方法是什么?如果我从列表中删除它将使我的循环失效?

std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());

for (IInventory* index : inv)
{
    // Do some stuff
    // OK, I decided I need to remove this object from 'inv'...
}
Run Code Online (Sandbox Code Playgroud)

Set*_*gie 84

不,你不能.基于范围for是指您需要访问容器的每个元素一次.

for如果需要随意修改容器,多次访问元素,或者以非线性方式遍历容器,则应使用普通循环或其中一个表兄弟.

例如:

auto i = std::begin(inv);

while (i != std::end(inv)) {
    // Do some stuff
    if (blah)
        i = inv.erase(i);
    else
        ++i;
}
Run Code Online (Sandbox Code Playgroud)

  • 不喜欢这个解决方案,大多数容器都是O(N ^ 2).`remove_if`更好. (11认同)
  • 这个答案**是正确的,`erase`返回一个新的有效迭代器.它可能效率不高,但它可以保证有效. (5认同)
  • 不会删除 - 删除这里适用的习语吗? (4认同)
  • @Naveen我决定不尝试这样做,因为显然他需要遍历每个项目,用它进行计算,然后_possibly_从容器中删除它.擦除删除表示你只是擦除谓词返回"true",AFAIU的元素,这样看起来更好的方法是不将迭代逻辑与谓词混合. (4认同)
  • @SethCarnegie使用lambda删除谓词优雅地允许(因为这已经是C++ 11) (4认同)
  • @Kolyunya不行,因为他没有存储结束迭代器. (3认同)
  • @BenVoigt迭代器擦除绝不是O(n ^ 2),即使对于向量,它对于迭代器之后的其余元素也只是线性的.对于地图和集合,它是分摊的常量时间.也就是说,`remove_if`通常更清晰.也许你想用钥匙去掉? (3认同)
  • 我应该澄清我先前的评论:**独立**删除O(N)项目,在这个答案中看到,花费O(N ^ 2).**协调删除多个项目,如`remove_if`中所示,可以是O(N) (3认同)
  • 从向量中擦除会使擦除点处或之后的迭代器和引用无效,包括end()迭代器,不是吗?你的代码不正确吗? (2认同)

Bra*_*vic 53

每次从向量中移除元素时,必须假定擦除元素处于或之后的迭代器不再有效,因为移动了擦除元素之后的每个元素.

基于范围的for循环只是使用迭代器的"正常"循环的语法糖,因此上述情况适用.

话虽这么说,你可以简单地说:

inv.erase(
    std::remove_if(
        inv.begin(),
        inv.end(),
        [](IInventory* element) -> bool {
            // Do "some stuff", then return true if element should be removed.
            return true;
        }
    ),
    inv.end()
);
Run Code Online (Sandbox Code Playgroud)

  • "*因为向量可能重新分配了内存块,它保留了它的元素*"不,由于调用`erase`,`vector`永远不会重新分配.迭代器失效的原因是因为移动了擦除元素之后的每个元素. (5认同)
  • `[&amp;]` 的默认捕获将是合适的,以允许他使用局部变量“做一些事情”。 (2认同)
  • 这看起来并不比基于迭代器的循环更简单,此外,您还必须记住将`remove_if`括在`.erase'中,否则什么也不会发生。 (2认同)
  • @bobobobo如果用"基于迭代器的循环"你的意思是[Seth Carnegie的回答](http://stackoverflow.com/a/10360466/533120),那就是平均O(n ^ 2).`std :: remove_if`是O(n). (2认同)

dir*_*tly 14

理想情况下,迭代时不应修改向量.使用擦除删除习语.如果你这样做,你可能会遇到一些问题.由于在vector一个erase无效与元素开头的所有迭代器被擦除高达的end(),你需要确保你的迭代器的使用仍然有效:

for (MyVector::iterator b = v.begin(); b != v.end();) { 
    if (foo) {
       b = v.erase( b ); // reseat iterator to a valid value post-erase
    else {
       ++b;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您需要按b != v.end()原样进行测试.如果您尝试按如下方式对其进行优化:

for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
Run Code Online (Sandbox Code Playgroud)

因为你e在第一次erase通话后失效了,你会遇到UB .

  • 这不是擦除删除习语。没有调用 `std::remove`,它是 O(N^2) 而不是 O(N)。 (2认同)

Yex*_*exo 5

在该循环中删除元素是否严格要求?否则,您可以将要删除的指针设置为NULL,并对向量进行另一次传递以删除所有NULL指针.

std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );

for ( IInventory* &index : inv )
{
    // do some stuff
    // ok I decided I need to remove this object from inv...?
    if (do_delete_index)
    {
        delete index;
        index = NULL;
    }
}
std::remove(inv.begin(), inv.end(), NULL);
Run Code Online (Sandbox Code Playgroud)