简体   繁体   English

C ++如何在迭代时从向量中擦除

[英]C++ how to erase from vector while iterating

std::vector<int *>intList;
int *a = new int;
*a = 3;
int *b = new int;
*b = 2;
int *c = new int;
*c = 1;

intList.push_back(a);
intList.push_back(b);
intList.push_back(c);

std::vector<int *>::iterator it;

for (it = intList.begin(); it != intList.end();)
{
    int *a = (int*)*it;

    if ((*a) == 2)
    {
        delete a;
        intList.erase(it);
    }
    else
    {
        ++it;
    }
}

This code crashes at the start of the for loop when something is erased from the vector. 当从向量中删除某些内容时,此代码在for循环的开头崩溃。 I do not understand why. 我不理解为什么。 I am using Visual Studio 2015 if that helps 我正在使用Visual Studio 2015,如果有帮助

erase returns next iterator. erase返回下一个迭代器。

if ((*a) == 2)
{
    delete a;
    it = intList.erase(it);
}

EDIT: remove() and remove_if() will copy the elements(pointer here) and one will end up with multiple elements pointing to same integer and if you then try to free them, you'll be left with dangling pointers. 编辑: remove()remove_if()将复制元素(这里的指针),最后将指向多个指向相同整数的元素,如果您随后尝试释放它们,则将剩下悬挂的指针。

Consider the vector has 4 elements which look something like 考虑向量有4看起来像的元素

0x196c160 0x196bec0 0x196c180 0x196bee0 

One might be tempted to use erase-remove idiom 一个人可能很想使用erase-remove惯用语

auto temp = remove_if(vec.begin(),vec.end(),[](const auto &i){return *i==2;});

Now it looks like 现在看起来像

0x144aec0 0x144b180 0x144b180 0x144aee0

temp would be pointing to 3rd element and a temp将指向3rd元素和一个

for(auto it=temp;it!=vec.end();it++)
    delete *it;

Now the second element is a dangling pointer. 现在,第二个元素是悬空指针。

EDIT 2: The above problem could be solved if you delete before the element is copied.Look at @Dietmar's answer. 编辑2:如果您在复制元素之前delete ,可以解决以上问题。@ Dietmar的答案。

Better to use std::vector<std::unique_ptr<int>> (or even std::vector<int> if you don't need pointer). 最好使用std::vector<std::unique_ptr<int>> (如果不需要指针,甚至使用std::vector<int> )。

then just use erase-remove idiom: 然后只需使用擦除删除惯用语:

std::vector<int> intList{3, 2, 1};

intList.erase(std::remove(intList.begin(), intList.end(), 2), intList.end());

or 要么

std::vector<std::unique_ptr<int>> intList;
intList.puch_back(std::make_unique<int>(3));
intList.puch_back(std::make_unique<int>(2));
intList.puch_back(std::make_unique<int>(1));

intList.erase(std::remove_if(intList.begin(), intList.end(),
                             [](const auto& p){ return *p == 2; }),
              intList.end());

If you really need raw owning pointer, you may use a variant using partition : 如果您确实需要原始拥有指针,则可以使用带有partition的变体:

std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };

auto it = std::partition(intList.begin(), intList.end(),
                         [](const auto& p){ return *p != 2; });

std::foreach (it, intList.end(), [](int* p) { delete p; });
intList.erase(it, intList.end());

Finally, if you really need to do it manually, you have to fix your erase line to: 最后,如果您确实需要手动执行此操作,则必须将擦除行固定为:

it = intList.erase(it);

to have: 拥有:

std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };

for (auto it = intList.begin(); it != intList.end(); /*Empty*/) {
    int *p = *it;

    if (*p == 2) {
        delete p;
        it = intList.erase(it);
    } else {
        ++it;
    }
}

That code is causing iterator invalidation. 该代码导致迭代器无效。

You're deleting a then expecting your iterator (which is just a pointer) to know what just happened. 您正在删除一个,然后希望您的迭代器(只是一个指针)知道发生了什么。

If you have to iterate through the whole vector then consider a temporary vector of what to keep/throwaway and use that. 如果您必须遍历整个向量,则考虑保留/丢弃并使用该向量的临时向量。

Or better yet just use find http://www.cplusplus.com/reference/algorithm/find/ 或者更好的方法是使用http://www.cplusplus.com/reference/algorithm/find/

If you take a look at documentation the erase function returns next iterator. 如果您查看文档,则擦除功能将返回下一个迭代器。 In your case using it = intList.erase(it) is the solution. 在您的情况下,使用it = intList.erase(it)是解决方案。 After c++11 all erase functions from other containers follow the same idea. 在c ++ 11之后,其他容器中的所有擦除功能都遵循相同的想法。

The simple anser is: you don't! 简单的答案是:您不要! Instead you use a two stage approach: first get rid of the elements, then resize the container. 取而代之的是,您使用两步方法:首先删除元素,然后调整容器的大小。 The use of raw pointers slightly complicates things but it is still doable: 使用原始指针会使事情稍微复杂一些,但仍然可行:

auto end = std::remove_if(intList.begin(), intList.end(),
    [](int *ptr){ return *ptr == 2 && (delete ptr, true); });
intList.erase(end, endList.end());

Trying to erase individual elements while iterating over std::vector has non-linear worst case complexity. std::vector上进行迭代时尝试擦除单个元素具有非线性最坏情况的复杂性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM