我有这样的代码:

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::列表?


当前回答

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

其他回答

你可以写

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

std::vector::erase std::remove_if习语应该在items是一个向量而不是一个列表时使用,以保持复杂度在O(n) -或者在您编写通用代码时,items可能是一个容器,没有有效的方法来删除单个项(如vector)。

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

下面是一个使用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);

你需要结合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 != 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);
    }
}

当然,最有效和超级酷®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)));

删除只会使指向被删除元素的迭代器失效。

所以在这种情况下,删除*i后,i是无效的,你不能对它做增量操作。

你能做的是首先保存要删除的元素的迭代器,然后增加迭代器,然后删除保存的迭代器。

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

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