【问题标题】:c++ vector erase advances iteratorc++ 矢量擦除推进迭代器
【发布时间】:2019-10-25 15:33:14
【问题描述】:

以下简化代码有效,因为它删除了所有矢量元素。但是,我不明白为什么。由于f.erase(r) 没有捕获返回的值,这将是新的迭代器值,并且没有其他迭代器增量器,并且according to documentation, erase(iterator position) 参数不是通过引用传递的,迭代器在哪里得到高级?

#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> f = {1,2,3,4,5};
  auto r = f.begin();
  while (r != f.end())
  {
    std::cout << "Erasing " << *r << std::endl;
    f.erase(r);
  }
  return 0;
}

【问题讨论】:

  • 欢迎来到未定义的行为领域,您想要的结果甚至可以是您得到的结果。如果你想清空一个向量,只需使用clear() -> f.clear();
  • erase 使迭代器无效,因此您不应在此之后使用 r。 “它有效”是未定义行为的最糟糕体现

标签: c++ vector iterator erase


【解决方案1】:

迭代器在哪里得到高级?

它没有,迭代器一直指向同一个位置。这在技术上是未定义的行为,但如果您考虑一下循环实际在做什么,您就会明白为什么会得到“正确”的结果。

你的向量包含一个指向它存储的对象的指针。您的迭代器将使用您想要的元素的偏移量指向该内存。在这种情况下,它将指向数据的开头。当您擦除第一个元素时,迭代器无效,但它仍指向向量的开头。 erase 将所有元素向前移动,因此当您进入下一次迭代时,您将处于与第一次迭代中相同的状态,除了向量小一个元素。您反复执行此操作,直到没有剩余元素和end() == begin()

您不应该依赖这种情况总是发生,而只需使用 clear() 从向量中删除所有元素。

【讨论】:

    【解决方案2】:

    迭代器在哪里升级?

    迭代器没有进阶。您的代码似乎工作的事实只是偶然,实际上您有未定义的行为。

    来自cppreferencestd::vector::erase

    在擦除点或之后使迭代器和引用无效,包括 end() 迭代器。

    在调用f.erase(r); 后,您不能再使用r。如果这样做,可能会发生有趣的事情。

    【讨论】:

      【解决方案3】:

      你必须写

        while (r != f.end())
        {
          std::cout << "Erasing " << *r << std::endl;
          r = f.erase(r);
         ^^^^^^^^^^^^^^^^
        }
      

      因为擦除后迭代器就失效了。

      或者你可以写

      f.clear();
      

      因为循环删除了向量的所有元素。

      考虑到迭代器 r 仅在循环中使用,最好在使用它的循环范围内声明它。例如

        for ( auto r = f.begin(); r != f.end(); )
        {
          std::cout << "Erasing " << *r << std::endl;
          r = f.erase(r);
        }
      

      【讨论】:

        【解决方案4】:

        according to documentation

        你应该读到最后:

        vector::erase - C++ Reference

        因为向量使用数组作为其底层存储,擦除除vector end 以外的位置的元素会导致容器在擦除段后将所有元素重新定位到新位置。与其他类型的序列容器(例如listforward_list)为相同操作执行的操作相比,这通常是一种低效的操作。

        返回值

        一个迭代器,指向函数调用删除的最后一个元素之后的元素的新位置。如果操作删除了序列中的最后一个元素,则为container end

        vector::erase - C++ Reference

        最后:

        迭代器有效性

        指向position(或first)及以后的迭代器、指针和引用无效,所有迭​​代器、指针和引用在position之前的元素em>(或 first)保证会继续引用他们在调用之前引用的相同元素。

        【讨论】:

          【解决方案5】:

          在大多数情况下(包括erase())在向量中添加/删除元素会使引用和迭代器无效。使用旧的迭代器会导致未定义的行为。

          正如@Nathan 在 cmets 中提到的那样,f.clear() 就是您所需要的。

          【讨论】:

            【解决方案6】:

            将迭代器传递给erase 会使该迭代器无效,因此进一步使用它(在下一次迭代中将它传递给erase 时)具有未定义的行为。因此,该程序实际上并没有“工作”。它可能看起来有效,因为这是一种可能的行为。但不能保证这种行为。

            【讨论】:

              【解决方案7】:

              erase 使r 无效。在此之后取消引用 r 会导致未定义的行为。


              但在现实世界中,除非实现有意检查迭代器的有效性,否则它不会引起任何问题。

              vector 迭代器通常只存储指向元素的指针。当你删除一个元素时,它右边的所有元素都会向左移动一个位置。因此,原来被删除元素占用的内存位置,将被下一个元素占用。

              【讨论】:

              • 为什么投反对票?现在解释UB被认为是异端吗? :P
              • 没有投反对票,但 “它不应该引起任何问题” 是......只是没有。而不仅仅是因为检查版本中的误报。
              • @MaxLanghof 我想不出它会破坏什么的任何其他原因。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-01-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多