【问题标题】:Vector erase iterator向量擦除迭代器
【发布时间】:2021-01-02 05:01:20
【问题描述】:

我有这个代码:

int main()
{
    vector<int> res;
    res.push_back(1);
    vector<int>::iterator it = res.begin();
    for( ; it != res.end(); it++)
    {
        it = res.erase(it);
        //if(it == res.end())
        //  return 0;
    }
}

“一个随机访问迭代器,指向函数调用擦除的最后一个元素之后的元素的新位置,如果操作擦除了序列中的最后一个元素,则该位置是向量结束。”

此代码崩溃,但如果我使用 if(it == res.end()) 部分然后返回,它可以工作。怎么会? for 循环是否缓存了res.end(),所以不等于运算符会失败?

【问题讨论】:

标签: c++ vector iterator


【解决方案1】:

res.erase(it) 总是返回下一个有效的迭代器,如果你删除最后一个元素,它将指向.end()

在循环结束时总是调用++it,所以你增加了.end(),这是不允许的。

尽管简单地检查 .end() 仍然会留下一个错误,因为您总是在每次迭代时跳过一个元素(it 通过从 .erase() 返回“递增”,然后再次通过循环)

你可能想要这样的东西:

 while (it != res.end()) {
        it = res.erase(it);    
 }

删除每个元素

(为了完整性:我假设这是一个简化的示例,如果您只是希望每个元素都消失而不必对其执行操作(例如删除),您应该简单地调用res.clear()

当你只有条件地擦除元素时,你可能想要类似的东西

for ( ; it != res.end(); ) {
  if (condition) {
    it = res.erase(it);
  } else {
    ++it;
  }
}

【讨论】:

  • 好的,所以它首先递增,然后在递增之后进行比较
  • 不,喜达屋;您的代码正在尝试一一删除向量中的所有元素。为此,您应该从res.begin() 开始,然后永远不要推进迭代器,而是检索在擦除元素时返回的迭代器(所有 STL 容器也是如此)。增量本身就是错误的部分。
  • 在真正的代码中,我并不是要删除所有元素,但是谢谢,我明白我现在做错了什么
  • 嗨,我也是这样做的,但我仍然收到“out_of_range”错误。你能告诉我为什么吗?
  • @DukeLover 您必须在某处等于.end() 时执行iterator++,而没有看到任何我能猜到的代码。如果您无法弄清楚,也许可以提出一个问题?
【解决方案2】:
for( ; it != res.end();)
{
    it = res.erase(it);
}

或者,更一般地说:

for( ; it != res.end();)
{
    if (smth)
        it = res.erase(it);
    else
        ++it;
}

【讨论】:

  • 为什么不使用while
  • @chamini2 在这种情况下,while 循环是等效的。
【解决方案3】:

因为vector中的erase方法返回了传递的迭代器的下一个迭代器。

我将举例说明如何在迭代时删除向量中的元素。

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};

    //method 1
    for(auto it = vecInt.begin();it != vecInt.end();){
        if(*it % 2){// remove all the odds
            it = vecInt.erase(it); // note it will = next(it) after erase
        } else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 2
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 2
    for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
        if (*it % 2){
            it = vecInt.erase(it);
        }else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 3
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 3
    vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
                 [](const int a){return a % 2;}),
                 vecInt.end());

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

}

在下面输出aw:

024
024
024

一个更生成的方法:

template<class Container, class F>
void erase_where(Container& c, F&& f)
{
    c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
            c.end());
}

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
    //method 4
    auto is_odd = [](int x){return x % 2;};
    erase_where(vecInt, is_odd);

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;    
}

【讨论】:

    【解决方案4】:

    it++ 指令在块的末尾完成。因此,如果您要擦除最后一个元素,则尝试递增指向空集合的迭代器。

    【讨论】:

      【解决方案5】:

      现代 C++ 可以做的事情是使用“std::remove_if”和 lambda 表达式;

      此代码将删除向量的“3”

      vector<int> vec {1,2,3,4,5,6};
      
      vec.erase(std::remove_if(begin(vec),end(vec),[](int elem){return (elem == 3);}), end(vec));
      

      【讨论】:

        【解决方案6】:

        不要擦除然后递增迭代器。无需增加,如果您的向量有奇数(或偶数,我不知道)元素,您将错过向量的末尾。

        【讨论】:

          【解决方案7】:

          您在 for 循环的循环表达式中将 it 递增到(空)容器的末尾。

          【讨论】:

            【解决方案8】:

            以下方法似乎也有效:

            for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
            {
              res.erase(it--);
            }
            

            不确定这是否有任何缺陷?

            【讨论】:

            • 虽然这段代码可以回答这个问题,但最好解释一下它的作用并添加一些参考。
            • 我不确定上面的代码。我看到了 3 个主要问题。首先,您不会在删除后将 res.erase(it) 重新签名回it。在删除内容时,您不能在迭代器语句中包含 it++,因此您应该进行条件检查以删除它。如果条件失败,那么您将迭代到下一个 (it++)。虽然我想知道为什么你有it--?请原谅我,但你为什么还要减少迭代器?也许我绊倒了,如果是这样我道歉。
            • @VG 谢谢,我想您的评论解决了答案中的问题,因此使其具有教育意义,也许值得一提。恐怕我也看不懂it--的逻辑了,从此桥下流过太多水了……
            • @SkippyleGrandGourou 感谢您的回复,我还没有真正找到符合上述减量状态的内容。是否可以在删除后迭代回一步?也许它与it = res.erase(it) 相同?虽然我真的很怀疑。嗯
            • @VG :根据 Pieter 的回答,“res.erase(it) 总是返回下一个有效的迭代器”。我猜it--it++ 取消,所以据我了解,这段代码一直在擦除(新的)第一个元素。执行it-- 似乎不是一个好主意,不过,it 现在是第一个元素……
            【解决方案9】:
            if(allPlayers.empty() == false) {
                for(int i = allPlayers.size() - 1; i >= 0; i--)
                {
                    if(allPlayers.at(i).getpMoney() <= 0) 
                        allPlayers.erase(allPlayers.at(i));
                }
            }
            

            这对我有用。并且不需要考虑索引已经被删除了。

            【讨论】:

            • 你怎么能说这对你有用?你从来没有测试过。这甚至不编译。 alllPlayers.at(i) 不返回迭代器。但是 erase() 需要一个迭代器。
            【解决方案10】:

            作为对 crazylammer 答案的修改,我经常使用:

            your_vector_type::iterator it;
            for( it = res.start(); it != res.end();)
            {
                your_vector_type::iterator curr = it++;
                if (something)
                    res.erase(curr);
            }
            

            这样做的好处是您不必担心忘记递增迭代器,从而在您有复杂逻辑时减少错误的发生。在循环内部,curr 永远不会等于 res.end(),并且无论您是否从向量中删除它,它都将位于下一个元素处。

            【讨论】:

            猜你喜欢
            • 2014-10-23
            • 1970-01-01
            • 2013-02-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多