从列表容器中删除元素

时间:2013-01-14 13:20:34

标签: c++ list containers erase

我很难理解为什么代码会以这种方式运行。首先,我已经阅读了相关的回答材料,但仍然发现了解释。所以我想知道是否有人能够以简单的方式解释这一点。

好的,我正在从列表中删除元素。

该列表包含奇数和偶数的int元素。这一部分我明白了。 这是我最初用来删除列表中奇数的代码

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
    {
        if(*i%2 == 0 )
        {
             lNo.erase(i);
        }
        else
        {
             cout << " " << *i;
        }
     }

使用此代码,程序根本无法编译,我读了一条消息,说明程序必须关闭。

擦写功能在我编写此代码时起作用:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
    {
        if(*i%2 == 0 )
        {
             i = lNo.erase(i);  
        }
        else
        {
            cout << " " << *i;
        }
   }

我只需要理解为什么程序在我编码i = lNo.erase(i)而不是只用lNo.erase(i)时有效?

非常感谢一个简单的简洁答案。 我知道不同的容器有不同的约束,所以我违反了原始代码的哪些约束?。

3 个答案:

答案 0 :(得分:6)

the documentation中所述,erase函数使传入的迭代器无效。这意味着它不能再次使用。循环不能继续使用该迭代器。

该文档还声明它将一个迭代器返回到擦除后的元素。该迭代器有效并可用于继续。

但是请注意,因为它在删除之后的元素之后返回一个迭代器,所以不需要将其递增以进行前进,或者不会检查该元素是否有奇怪性。循环应该抓住它,只有在没有擦除时才会增加。

答案 1 :(得分:5)

即使您的第二个代码也不正确。

正确的代码应为:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); /*NOTHING HERE*/ )
{
    if(*i%2 == 0 )
    {
         i = lNo.erase(i);  
    }
    else
    {
        cout << " " << *i;
        ++i; //INCREMENT HERE, not in the for loop
    }
}

请注意erase()将删除该项并将迭代器返回到下一项。这意味着,擦除时,您不需要在代码中增加i;相反,您只需要使用i返回的值更新erase

您可以将erase-remove idiom用作:

lNo.erase(std::remove_if(lNo.begin(),
                         lNo.end(),
                         [](int i) { return i%2 == 0; }), 
                         lNo.end());

Live demo

答案 2 :(得分:0)

问题是你正在使用一个不希望修改列表链接的迭代器。 因此,当您在列表中调用erase()时,链接会被有效修改,因此迭代器不再有效。 i ++语句不再起作用了。

但是,在第二个版本中,您将迭代器重新分配给仍然具有完整链接的有效对象,因此i ++语句仍然可以工作。

在某些框架中,您有两种迭代器,它们会立即反映底层数据集发生的事情(这是您正在使用的内容),以及不会改变其链接的类型。底层数据集(因此您不必使用第二版的奇怪技巧)。