3

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

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

该列表包含奇数和偶数的 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) 时程序可以工作?

一个简单简洁的答案将不胜感激。我知道不同的容器有不同的约束,那么我在原始代码中违反了哪个约束?

4

3 回答 3

6

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

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

但是请注意,由于它在被擦除的元素之后返回一个迭代器,因此不需要增加它来推进,否则不会检查该元素的奇数。循环应该对此进行处理,并且仅在未完成擦除时才增加。

于 2013-01-14T13:27:11.010 回答
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());

现场演示

于 2013-01-14T13:26:12.207 回答
0

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

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

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

于 2013-01-14T13:32:17.660 回答