std::list 反向迭代和擦除导致崩溃
std::list reverse iterating & erasing causes 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
在 VS2010 下,它会在第一次循环传递时抛出异常:
l.erase((*rj)->iter);
if ( ri == l.rend() ) // exception
这应该让你大致了解发生了什么。你看,reverse_iterator
只是标准迭代器的包装器。也就是说,你应该记住,它有base()
返回底层迭代器的成员 - 你不必像在结构中那样将其存储在其他地方node
。
这是reverse_iterator
与iterator
的关系的一个很好的答案。在您的情况下,rbegin
将基于begin
迭代器。如果从列表中删除begin
(这样做,因为它只有一个元素),则基于此iterator
的所有reverse_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;
}
反向迭代器(tpyly)不是一个唯一的类,而是普通迭代器上的适配器 - 它有一个迭代器作为成员进入列表,并使用它来执行自己的移动和取消引用。因此,当此列表迭代器失效时,反向迭代器也会失效。
我正在写一个答案来记录我的发现;如果你遇到这个问题,请尝试
SingerOfTheFall建议的方法,它就像一个魅力,例如:
for(auto it=values.end();it!=values.begin();){
if((*it).second.endPoint)
break;
values.erase((*(it--)).first);
}
回到我对此的发现:
当我遇到程序挂起的问题时,我已经运行了valgrind
检查,它删除了一些源自libstdc++
的奇怪Invalid reads
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()
不会停止迭代器,并且++
操作被困在循环中
您需要将擦除返回值存储到迭代器中。执行以下更改。
(*rj)->iter= l.erase((*rj)->iter);
- 引用一个已擦除类型(void*)的指针
- 擦除while循环中迭代的元素
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- C++擦除(如果存在)
- 在映射擦除c++期间执行循环的次数
- 为什么擦除方法会影响结束方法
- C++ 字符串类擦除成员函数的时空复杂性
- 类型擦除的std::function与虚拟函数调用的开销
- C++14 中unordered_map矢量和擦除删除成语的奇怪行为
- C++ 擦除函数中需要澄清
- 循环挂起迭代的 std::擦除 on std::list
- 擦除许多矢量元素,同时使用'auto'
- 如何擦除冗余输入?
- 迭代器擦除矢量中的元素无效,但它不会崩溃
- std::在循环中计算重复字符时擦除崩溃
- 在小对象优化中调试崩溃以进行类型擦除
- 矢量未正确擦除内容(复制分配运算符的量运行直到崩溃 [BEX])
- std::list 反向迭代和擦除导致崩溃
- c++矢量擦除崩溃程序
- 在映射中擦除时崩溃(c++)