使用" pop_front"从排序列表中删除元素导致崩溃

时间:2015-12-10 14:30:39

标签: c++

我需要从排序列表中删除前3项,为此逻辑我可以使用' erase'删除元素。但是,我想知道' pop_front'适用于这种情况。因此,我尝试了下面的代码,但它崩溃了(在一个循环后破坏了迭代器)。在这种情况下,我不知道为什么它会破坏迭代器。有人可以帮我这个。

int main(int argc, char *argv[])
{
    list<int> l;
    for (int i=1; i<=5; ++i)
    {
        l.push_back(i);
    } 

    for (list<int>::iterator itr=l.begin(); itr != l.end(); ++itr)
    {
        if(*itr <= 3)
          l.pop_front(); 
    }
    return 0;
}

3 个答案:

答案 0 :(得分:2)

  

在这种情况下,我不知道它为什么会破坏迭代器。

请阅读documentation

  

擦除元素的引用和迭代器无效。

第一次迭代中的

itr指向擦除的l.begin(),然后无效迭代器上的++itr失败。您需要在调用pop_front()之前增加迭代器。但更好的是使用迭代器显式擦除以避免意外:

for (list<int>::iterator itr=l.begin(); itr != l.end(); )
{
    if(*itr <= 3) l.erase( it++ );
    else ++it; 
}

此代码的行为可能与您的行为不同,因为无论itr指向何处,您始终会删除第一个元素,但我认为您要删除小于或等于3的元素。

答案 1 :(得分:0)

所以这里有一些事情发生。

首先:你在迭代它时改变一个列表。通常,尝试这是一个坏主意。将列表视为链接列表。每个节点由其自己的值和指向下一个元素的指针/引用组成。通过弹出一个项目,你将其从列表中删除,然后迭代器将不再知道在哪里寻找下一个元素。它实际上比这更复杂(每种类型的标准集合都有某些定义的操作必须使迭代器无效,但它们在集合类型之间有所不同),但这是一般的想法。

第二:你没有删除索引小于3的元素,你试图删除值小于3的元素。在这种情况下逻辑上会起作用,因为前3个元素的值为1,2 ,和3。

为了解决你的问题,我会写下你的第二个循环:

for(int index=0; index<3; index++){
  l.pop_front();
}

然后,如果您需要执行其他操作,则可以稍后遍历列表。

答案 2 :(得分:0)

感谢您的回复,

我想我找到了问题的答案:

答案:在循环中'使用列表开始启动迭代器'并在for循环块中我使用pop_front删除'list的第一个元素',一旦我们删除了第一个元素list.begin()更改为list中的下一个元素。但仍然没有迭代器。所以它变成悬空指针,接下来我们在下一个循环中访问悬空指针,因此它是转储代码。

通过gdb找到上面的信息。

Breakpoint 2, main (argc=1, argv=0x7fffffffdfd8) at list_erase.cpp:20
20      for (list<int>::iterator itr=l.begin(); itr != l.end(); ++itr)
(gdb) n
22          if(*itr <= 3)
(gdb) p *itr
$5 = (int &) @0x603020: 1
(gdb) p itr
$6 = {_M_node = 0x603010}
(gdb) p l.begin()
$7 = {_M_node = 0x603010}
(gdb) n
23              l.pop_front();
(gdb) n
20      for (list<int>::iterator itr=l.begin(); itr != l.end(); ++itr)
(gdb) p itr
$8 = {_M_node = 0x603010}
(gdb) p l.begin()
$9 = {_M_node = 0x603030} 
(gdb) n
22          if(*itr <= 3)
(gdb) p itr
$10 = {_M_node = 0x0}
(gdb) p l.begin()
$11 = {_M_node = 0x603030}

要通过'pop_front'修复我的问题,我应该这样做:

 for (list<int>::iterator itr=l.begin(); itr != l.end(); )
 {
    if(*itr <= 3)
    {
       l.pop_front();
       itr = l.begin();
       //  OR
       // itr = l.erase(l.begin()); 
    }else
       ++itr;
 }

'pop_front就像这样工作': 因为,std :: list pop_front实现如下:

 void pop_front() { erase(begin()); } 

在pop_front代码实现中,我们没有捕获列表的下一个元素的返回值,因此在擦除之后它将是悬空指针。所以,预计崩溃。

让我知道,如果错了..