擦除 std::list::iterator 不会使迭代器无效并销毁对象吗?
为什么下面打印2
?
list<int> l;
l.push_back( 1 );
l.push_back( 2 );
l.push_back( 3 );
list<int>::iterator i = l.begin();
i++;
l.erase( i );
cout << *i;
我知道 erase
返回什么,但我想知道为什么这样可以?或者它是未定义的,还是取决于编译器?
Why does the following print 2
?
list<int> l;
l.push_back( 1 );
l.push_back( 2 );
l.push_back( 3 );
list<int>::iterator i = l.begin();
i++;
l.erase( i );
cout << *i;
I know what erase
returns, but I wonder why this is OK? Or is it undefined, or does it depend on the compiler?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
是的,这是未定义的行为。您正在取消引用一种野指针。
erase
后不应使用i
的值。是的,
擦除
破坏指向的对象。然而,对于 POD 类型,销毁不会执行任何操作。erase
不会为被擦除的迭代器分配任何特殊的“null”值,迭代器只是不再有效。Yes, it's undefined behaviour. You are dereferencing a kind of wild pointer. You shouldn't use the value of
i
aftererase
.And yes,
erase
destructs the object pointed to. However, for POD types destruction doesn't do anything.erase
doesn't assign any special "null" value to the iterator being erased, the iterator is just not valid any more.“销毁”一个对象意味着它的内存被回收并且它的内容可能会被改变(主要是如果手写的析构函数这样做,并且可能是由于将与空闲内存相关的东西存储在适当的位置)。 list::erase 返回一个新的迭代器,您应该使用它来代替作为参数传递的迭代器(我很想使
i = l.erase(i);
成为一种习惯)。破坏绝不意味着记忆被扫除、消灭。 从 CPU 的角度来看,在大多数情况下,以前的有效位置仍然有效(即它可以获取值),但不能依赖,因为其他操作可能会在任何时间出于任何目的回收该位置时间。
恕我直言,您不太可能看到 *i 抛出段错误——尽管这可能发生在使用指针的更复杂类型中,但您可能会看到它具有新值。
其他集合可能具有比列表更可预见的行为。 IIrc,向量会压缩存储区域,因此在极少数情况下,只有通过进一步取消引用
i
才能看到先前的值。"destroying" an object means its memory is reclaimed and its content might be altered (mainly if the hand-written destructor do so, and possibly as a result of storing free-memory-related stuff in place). list::erase returns you a new iterator that you should use instead of the one that was passed as argument (I'd be tempted to make
i = l.erase(i);
an habbit).In no way do destruction imply that memory is swept, wiped out. Previously valid location are still valid in most cases from the CPU's point of view (ie. it can fetch values), but can't be relied on because other operation may recycle that location for any purpose at any time.
You're unlikely to see
*i
throwing a segfault, imho -- although that might happen with more complex types that use pointers, but you might see it having new values.Other collections might have a more previsible behaviour than list. IIrc, a vector would compact the storage area so the previous value would only be seen by further dereferencing
i
in rare cases.看起来迭代器仍然指向这个内存....
如果您要向此块写入一些内容,
也许下次*我会抛出分段错误..
但对猜测表示抱歉
Seems like iterator still points to this memory....
If you would write something to this block,
maybe next time *i would throw segmentation fault..
sorry for speculation though
由于您正在处理链接列表,因此列表的元素不需要在内存中彼此位于“后面”。如果您对向量尝试相同的操作,您可能(因为行为未定义)
会被打印为 2。
但是,这不是一种非常安全的编程方式。因此,一旦删除迭代器,请确保不再使用它,除非您使用 begin() 或 end() 等再次初始化它。
Since you are dealing with a linked list, the elements of the list need not to be right "behind" each other in memory. If you would try the same thing with a vector, you would likely (since the behavior is undefined) experience
to be printed as 2.
However, this is not a very safe way of programming. So once you erase an iterator make sure not to use it again, unless you initialize it again with begin() or end() etc.
尝试使用正确的选项和良好的编译器来编译代码,
然后运行它。 (对于 VC++,
/D_DEBUG /EHs /MDd
似乎是充足的。使用 g++,<代码>-D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG
至少是-D_GLIBCXX_DEBUG_PEDATIC。两个编译器都需要更多
一般选项。)它应该崩溃。 (我试过的时候确实如此。)
Try compiling your code with the correct options, with a good compiler,
then running it. (With VC++,
/D_DEBUG /EHs /MDd
seems to besufficient. With g++,
-D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG
, at least. Both compilers need even more-D_GLIBCXX_DEBUG_PEDANTIC
options in general.) It should crash. (It does when I tried it.)