我很难理解为什么代码会以这种方式运行。首先,我已经阅读了相关的回答材料,但仍然发现了解释。所以我想知道是否有人能够以简单的方式解释这一点。
好的,我正在从列表中删除元素。
该列表包含奇数和偶数的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)时有效?
非常感谢一个简单的简洁答案。 我知道不同的容器有不同的约束,所以我违反了原始代码的哪些约束?。
答案 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());
答案 2 :(得分:0)
问题是你正在使用一个不希望修改列表链接的迭代器。 因此,当您在列表中调用erase()时,链接会被有效修改,因此迭代器不再有效。 i ++语句不再起作用了。
但是,在第二个版本中,您将迭代器重新分配给仍然具有完整链接的有效对象,因此i ++语句仍然可以工作。
在某些框架中,您有两种迭代器,它们会立即反映底层数据集发生的事情(这是您正在使用的内容),以及不会改变其链接的类型。底层数据集(因此您不必使用第二版的奇怪技巧)。