2017-08-18 65 views
-1

当我在class vector容器中使用erase()方法时,出现段错误。如何安全地擦除std :: 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感兴趣。

+1

标准的方式来做到这就是所谓的[擦除删除成语(https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) – NathanOliver

+1

如果你真的想这样做你尝试这样做的方式,我会遍历外部循环中的vector1,以及内部循环中的vector 2。这样你就不会从外循环中删除矢量,并且会干扰迭代器的有效性。 – ttemple

回答

3

无法删除,如:

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

erase操作无效it,所以接下来++it是错误的。

相反的话,你可能会改写外循环为:

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

,改变itfor循环中:

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

DEMO


注意,你可能会使用remove-erase idio米还有:

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

这是一个标准的方式做这样的事情。

+0

谢谢!这解决了这个问题,现在我明白了 – EuGENE

1

问题是你的迭代器在擦除后无效。更改此:

vector2.erase(it); 

这样:

it = vector2.erase(it); 

那将是朝着解决问题迈出的一步。检查docs,你会发现返回值是下一个有效的迭代器。

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)); 
相关问题