std :: list reverse iterating&擦除导致崩溃

时间:2012-07-05 07:18:21

标签: c++ list crash

我使用反向迭代器遍历std :: list,并使用插入时获得的前向迭代器从列表中删除一些元素。示例程序如下所示。我读到从列表中删除元素不会使其他迭代器失效,除了那些引用被删除元素的迭代器。但是没有提到reverse_iterators,我的程序崩溃了。有人可以告诉我用法是否不正确?

程序正在做的是在列表中添加一个元素,存储其迭代器,反向迭代列表并使用其存储的迭代器删除列表中唯一的元素。

输出粘贴在代码示例下方。

#include <list>
#include <iostream>
using namespace std;

struct node
{
    int data;
    list<node*>::iterator iter;
} a;

int main()
{
    list<node*> l;
    a.data = 1;
    l.push_front( &a );
    a.iter = l.begin();
    list<node*>::reverse_iterator ri = l.rbegin();
    while ( ri != l.rend() )
    {
        cout << (*ri)->data << endl;
        list<node*>::reverse_iterator rj = ri;
        ++ri;
        if ( ri ==  l.rend() )
            cout << "before erase: reached end" << endl;
        l.erase((*rj)->iter);
        if ( ri ==  l.rend() )
            cout << "after erase : reached end" << endl;
        else
            cout << "after erase : Not reached end" << endl;
    }
}

输出

1
before erase: reached end
after erase : Not reached end
610568524
before erase : reached end
Segmentation fault

4 个答案:

答案 0 :(得分:2)

在VS2010下,它将在第一个循环传递中抛出异常:

 l.erase((*rj)->iter);
 if ( ri ==  l.rend() ) // exception

这应该可以让你大致了解发生了什么。你看,reverse_iterator只是标准迭代器的包装器。也就是说,您应该记住它有base()成员返回底层迭代器 - 您不必像在node struct中那样将其存储在其他位置。

Here's a great answer了解reverse_iteratoriterator的关系。在您的情况下,rbegin将基于begin迭代器。如果您从列表中删除begin(因为它只有一个元素),基于此reverse_iterator的所有iterator将变为无效 。牢记这一点,您可以通过以下方式重写循环:

while ( ri != l.rend() )
{  
    cout << (*ri)->data << endl;
    list<node*>::reverse_iterator rj = ri;
    ++ri;

    if ( ri ==  l.rend() )
        cout << "before erase: reached end" << endl;

    // the actual underlying iterator has an offset of one
    list<node*>::iterator it = l.erase(--rj.base());
    ri = reverse_iterator<list<node*>::iterator>(it);
    // or just
    // ri = reverse_iterator<list<node*>::iterator>(l.erase(--rj.base()));

    if ( ri ==  l.rend() )
        cout << "after erase : reached end" << endl;
    else
        cout << "after erase : Not reached end" << endl;
}

答案 1 :(得分:1)

反向迭代器(tpyically)不是唯一的类,而是普通迭代器上的适配器 - 它有一个迭代器作为成员进入列表并使用它来进行自己的移动和解除引用。因此,当此列表迭代器失效时,反向迭代器也会失效。

答案 2 :(得分:0)

我正在写一个答案来记录我的发现;如果你遇到这个问题,试试

SingerOfTheFall建议的方法,它就像一个魅力,例如:

        for(auto it=values.end();it!=values.begin();){
            if((*it).second.endPoint)
                break;
            values.erase((*(it--)).first);
        }

回到我的发现:

当我遇到问题时程序被挂起,并且我进行了valgrind检查,它删除了一些来自Invalid reads的奇怪libstdc++

Invalid read of size 8
    at 0x4EAA633: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20)
    by 0x402FEC: std::_Rb_tree_iterator<std::pair<int const, GroupControl::Group::Entry> >::operator--() (stl_tree.h:218)

我怀疑在最后一个元素擦除后rend()没有停止迭代器,而++ op被困在循环中

答案 3 :(得分:-1)

您需要将擦除返回值存储到迭代器中。做以下更改。

(*rj)->iter= l.erase((*rj)->iter);