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

3

我很难理解为什么代码会表现出这种行为。首先,我已经阅读了相关的答案材料,但仍然觉得解释有点高深。所以我想知道是否有人能够用简单的方式来解释一下。

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

列表包含既有奇数又有偶数的int元素。这部分我理解。 以下是我最初编写的从列表中删除奇数的代码:

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

使用这段代码,程序根本无法编译,并显示一条消息说明程序必须关闭。

当我写下这段代码时,erase函数就会起作用:

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个回答

6

文档所述,erase函数会使传入的迭代器失效,因此不能再使用该迭代器。循环也无法继续使用该迭代器。

该文档还指出,erase函数返回一个被删除元素之后的迭代器,该迭代器是有效的并可以继续使用。

注意,由于它返回一个在被删除元素之后的迭代器,因此不需要递增该迭代器来前进,否则该元素将不会被检查是否为奇数。循环应当考虑到这一点,并且只在没有进行删除时递增。


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;相反,您只需要使用从erase返回的值更新i即可。
您可以使用 erase-remove惯用语 如下:
lNo.erase(std::remove_if(lNo.begin(),
                         lNo.end(),
                         [](int i) { return i%2 == 0; }), 
                         lNo.end());

实时演示


0
问题在于你正在使用一个迭代器,它不希望列表的链接被修改。因此,当你在列表上调用erase()函数时,链接实际上被修改了,所以你的迭代器不再有效。i++语句也不再起作用。
但是,在第二个版本中,你重新将迭代器分配给仍然具有完整链接的有效对象,因此i++语句仍然可以正常工作。
在某些框架中,有两种类型的迭代器:一种是立即反映基础数据集发生的变化(这就是你正在使用的),而另一种则无论基础数据集发生什么变化,都不会改变它们的链接(因此你不必使用第二个版本的奇怪技巧)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接