如何安全地擦除std :: vector

时间:2017-08-18 18:33:33

标签: c++ c++11 vector iterator

我在erase()容器中使用class vector方法时出现段错误。

我比较了两个向量,所以我想从其中一个中删除另一个不存在的元素。为此,我使用了迭代器和erase(),如下所示:

#include <vector>

int main () {

std::vector<int> vector1 {6,7,5,44,3,10,9,17,1};
std::vector<int> vector2 {1,2,3,5,8};

for (std::vector<int>::iterator it (vector2.begin()); it != vector2.end(); ++it) {
    bool equal (false);
    for (std::vector<int>::iterator jt (vector1.begin()); jt != vector1.end(); ++jt) {
        if (*it == *jt) {
            equal = true;
            break       ;
        }
    }
    if (!equal) {
        vector2.erase(it);
    }
}

return 0;
}

导致段错误的原因是删除了vector28)中的最后一个元素,因为erase()无法成功地将迭代器从之前的end()位置移开(不再是新的。

如何防止这种情况?我知道unordered_set可能是此操作的适当容器,但我在vector感兴趣。

3 个答案:

答案 0 :(得分:3)

你无法删除:

if (!equal) {
    vector2.erase(it);
}

erase操作使it无效,因此下一个++it是错误。

而不是它,您可以将外循环重写为:

for (std::vector<int>::iterator it (vector2.begin()); it != vector2.end();)

并在it循环中更改for

if (!equal) {
    it = vector2.erase(it);
} else {
    ++it;
}

DEMO

注意,你也可以使用 remove-erase idiom

vector2.erase(
    std::remove_if(std::begin(vector2), std::end(vector2), [&vector1](const auto& e) {
        return std::find(std::cbegin(vector1), std::cend(vector1), e) == std::end(vector1);
    }),
    std::end(vector2)
);

这是执行此类操作的标准方法。

答案 1 :(得分:3)

正如其他人所指出的那样,问题在于擦除会使迭代器失效。但是在这里使用原始循环来进行这种操作有一个更普遍的问题。它需要大量的样板代码并且容易出错。您可以使用标准算法来避免这些陷阱。

std::vector<int> result;
std::sort(vector1.begin(), vector1.end());
std::sort(vector2.begin(), vector2.end());
std::set_difference(
  vector2.begin(),
  vector2.end(),
  vector1.begin(),
  vector1.end(),
  std::back_inserter(result));

答案 2 :(得分:2)

问题是擦除后你的迭代器失效了。改变这个:

vector2.erase(it);

到此:

it = vector2.erase(it);

这将是解决问题的一步。检查docs,您会看到返回值是下一个有效的迭代器。