你可以在迭代它时从std :: list中删除元素吗?

时间:2009-02-27 19:08:21

标签: c++ list std

我的代码看起来像这样:

for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
    bool isActive = (*i)->update();
    //if (!isActive) 
    //  items.remove(*i); 
    //else
       other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);

我想在更新后立即删除非活动项目,以避免再次走到列表中。但是如果我添加注释掉的行,当我到达i++时出现错误:“列表迭代器不可递增”。我尝试了一些在for语句中没有增加的替代项,但我无法正常工作。

当你走std :: list时,删除项目的最佳方法是什么?

13 个答案:

答案 0 :(得分:263)

首先必须增加迭代器(使用i ++),然后删除前一个元素(例如,使用i ++中返回的值)。您可以将代码更改为while循环,如下所示:

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive)
    {
        items.erase(i++);  // alternatively, i = items.erase(i);
    }
    else
    {
        other_code_involving(*i);
        ++i;
    }
}

答案 1 :(得分:120)

你想这样做:

i= items.erase(i);

这将正确地更新迭代器以指向您删除的迭代器之后的位置。

答案 2 :(得分:21)

你需要结合Kristo的答案和MSN:

// Note: Using the pre-increment operator is preferred for iterators because
//       there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
//       along the way you can safely save end once; otherwise get it at the
//       top of each loop.

std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end  = items.end();

while (iter != items.end())
{
    item * pItem = *iter;

    if (pItem->update() == true)
    {
        other_code_involving(pItem);
        ++iter;
    }
    else
    {
        // BTW, who is deleting pItem, a.k.a. (*iter)?
        iter = items.erase(iter);
    }
}

当然,效率最高的SuperCool®STL是这样的事情:

// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
    if (m_needsUpdates == true)
    {
        m_needsUpdates = other_code_involving(this);
    }

    return (m_needsUpdates);
}

// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));

答案 3 :(得分:10)

使用std :: remove_if算法。

修改 使用集合应该像: 准备收集。 2.流程收集。

如果你不混合这些步骤,生活会更容易。

  1. 的std ::的remove_if。或list :: remove_if(如果你知道你使用list而不是TCollection)
  2. 的std ::的for_each

答案 4 :(得分:5)

这是一个使用for循环的示例,该循环迭代列表并在遍历列表期间删除项目时递增或重新验证迭代器。

for(auto i = items.begin(); i != items.end();)
{
    if(bool isActive = (*i)->update())
    {
        other_code_involving(*i);
        ++i;

    }
    else
    {
        i = items.erase(i);

    }

}

items.remove_if(CheckItemNotActive);

答案 5 :(得分:4)

Kristo答案的循环版本的替代方案。

你会失去一些效率,你会在删除时再向前然后再转发,但是为了换取额外的迭代器增量,你可以在循环范围内声明迭代器并且代码看起来更清晰一些。选择什么取决于当下的优先事项。

答案完全没有时间,我知道......

typedef std::list<item*>::iterator item_iterator;

for(item_iterator i = items.begin(); i != items.end(); ++i)
{
    bool isActive = (*i)->update();

    if (!isActive)
    {
        items.erase(i--); 
    }
    else
    {
        other_code_involving(*i);
    }
}

答案 6 :(得分:2)

删除仅使指向已删除元素的迭代器无效。

因此,在这种情况下,删除* i后,i无效,您无法对其进行增量。

您可以做的是首先保存要删除的元素的迭代器,然后递增迭代器,然后删除保存的迭代器。

答案 7 :(得分:2)

如果您认为std::list类似于一个队列,那么您可以将所有要保留的项目出列并排队,但只能将要删除的项目出列(而不是入队)。这是一个示例,我想从包含数字1-10 ......

的列表中删除5
std::list<int> myList;

int size = myList.size(); // The size needs to be saved to iterate through the whole thing

for (int i = 0; i < size; ++i)
{
    int val = myList.back()
    myList.pop_back() // dequeue
    if (val != 5)
    {
         myList.push_front(val) // enqueue if not 5
    }
}

myList现在只有数字1-4和6-10。

答案 8 :(得分:2)

你可以写

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive) {
        i = items.erase(i); 
    } else {
        other_code_involving(*i);
        i++;
    }
}

您可以使用std::list::remove_if编写等效代码,该代码更简洁,更明确

items.remove_if([] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
});

当items是向量而不是列表时,应该使用std::vector::erase std::remove_if成语,以便在O(n)处保持compexity - 或者如果您编写通用代码并且项目可能是容器没有有效的方法来删除单个项目(如矢量)

items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
}));

答案 9 :(得分:2)

我已经总结了一下,这是带有示例的三种方法:

1。使用while循环

list<int> lst{4, 1, 2, 3, 5};

auto it = lst.begin();
while (it != lst.end()){
    if((*it % 2) == 1){
        it = lst.erase(it);// erase and go to next
    } else{
        ++it;  // go to next
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

2。在列表中使用remove_if成员功能:

list<int> lst{4, 1, 2, 3, 5};

lst.remove_if([](int a){return a % 2 == 1;});

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

3。将std::remove_if函数与erase成员函数结合使用:

list<int> lst{4, 1, 2, 3, 5};

lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
    return a % 2 == 1;
}), lst.end());

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

4。使用for循环,应注意更新迭代器:

list<int> lst{4, 1, 2, 3, 5};

for(auto it = lst.begin(); it != lst.end();++it){
    if ((*it % 2) == 1){
        it = lst.erase(it);  erase and go to next(erase will return the next iterator)
        --it;  // as it will be add again in for, so we go back one step
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2 

答案 10 :(得分:0)

执行while循环,它灵活,快速,易于读写。

auto textRegion = m_pdfTextRegions.begin();
    while(textRegion != m_pdfTextRegions.end())
    {
        if ((*textRegion)->glyphs.empty())
        {
            m_pdfTextRegions.erase(textRegion);
            textRegion = m_pdfTextRegions.begin();
        }
        else
            textRegion++;
    } 

答案 11 :(得分:0)

我想分享我的方法。该方法还允许在迭代过程中将元素插入到列表的后面

event

答案 12 :(得分:-4)

我认为你有一个错误,我这样编码:

for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
             itAudioChannel != audioChannels.end(); )
{
    CAudioChannel *audioChannel = *itAudioChannel;
    std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
    itAudioChannel++;

    if (audioChannel->destroyMe)
    {
        audioChannels.erase(itCurrentAudioChannel);
        delete audioChannel;
        continue;
    }
    audioChannel->Mix(outBuffer, numSamples);
}