从列表容器中删除一个元素

Erasing an element from a list container

本文关键字:一个 元素 删除 列表      更新时间:2023-10-16

我很难理解为什么代码是这样表现的。首先,我阅读了相关的回答材料,仍然发现解释有点超前。所以我想知道是否有人可以用一种简单的方式来解释这个问题。

好,我要从列表中删除元素。

列表包含奇数和偶数的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)?

请给我一个简明扼要的答复。我知道不同的容器有不同的约束,那么我在原始代码中违反了哪个约束呢?

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

文档还指出,它返回一个迭代器,指向被擦除的元素之后的元素。该迭代器有效,可用于继续操作。

但是请注意,由于它返回一个迭代器,指向被擦除的元素之后的元素,因此不需要对元素进行自增来前进,否则不会检查该元素的奇异性。循环应该考虑到这一点,并且只在没有擦除时才增加。

你的第二个代码也不正确。

正确的代码应该是这样的:
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;相反,您只需要使用erase的返回值更新i

您可以使用erase-remove idiom作为:

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

现场演示

问题是,您使用的迭代器不期望修改列表的链。所以当你在列表上调用erase()时,链被有效地修改了,所以你的迭代器不再有效。i++语句不再工作了

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

在某些框架中,你有两种迭代器,一种是立即反映底层数据集发生了什么(这是你正在使用的),另一种是无论底层数据集发生了什么,都不会改变它们的链接(所以你不必使用第二个版本的奇怪技巧)。