如何安全地擦除 std::vector 中的元素

How to safely erase elements in a std::vector

本文关键字:std vector 元素 擦除 何安全 安全      更新时间:2023-10-16

当我在class vector容器中使用erase()方法时,我遇到了段错误。

我正在比较两个向量,所以我想从其中一个向量中删除另一个向量中不存在的元素。为此,我使用迭代器和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;
}

导致段错误的是删除了vector2中的最后一个元素(8(,因为erase()无法成功地将迭代器从上一个end()位置(不再存在(移动到一个新的位置。

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

您不能像以下那样删除:

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

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

取而代之的是,您可以将外部循环重写为:

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

并在for循环中更改it

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

演示


请注意,您也可以使用删除-擦除习惯用语

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)
);

这是做这样的事情的标准方法。

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

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));

问题是您的迭代器在擦除后失效。更改此设置:

vector2.erase(it);

对此:

it = vector2.erase(it);

这将是朝着解决问题迈出的一步。检查文档,你会看到返回值是下一个有效的迭代器。