擦除 std::list::iterator 会使迭代器失效并销毁对象吗?

5
为什么以下代码会输出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返回什么,但我想知道为什么这样做是可以的?或者说它是未定义的,还是取决于编译器?

5个回答

15
是的,这是未定义的行为。您正在解引用一种野指针。在erase之后,不应使用i的值。
是的,erase destructs指向的对象。但是,对于POD类型,销毁不会有任何影响。 erase不会为被删除的迭代器分配任何特殊的“null”值,该迭代器只是不再有效。

erase 真的会销毁它所指向的对象吗?比如说一个指针列表,erase会销毁指针吗?我的理解是不会。 - Vite Falcon
@Vite:在这个上下文中,“really”是什么意思?erase调用迭代器目标的析构函数,但不会将迭代器目标所在的内存位置置空。请注意,如果列表包含指针,则delete指针并不等同于delete指针所指向的对象!销毁指针实际上是一个无操作。 - Vlad
1
@Vite Falcon 它破坏的是指针本身,而不是指针所指向的内容。 - Mark B
@Vlad - 当然,它看起来像是硬盘.. 10x - Kiril Kirov
@Kiril:我想清除内存可能被认为只是浪费时间 - 就像对文件系统一样。 - Vlad
显示剩余2条评论

2
"destroying"一个对象意味着它的内存被回收并且它的内容可能被改变(主要是如果手写的析构函数这样做,以及可能由于在原地存储空闲内存相关的东西而导致)。list :: erase返回一个新的迭代器,您应该使用它来替换作为参数传递的迭代器(我会倾向于使 = l.erase(i); 成为一种习惯)。
毁坏绝不意味着内存被清除,抹掉。从CPU的角度来看,在大多数情况下,先前有效的位置仍然有效(即,它可以获取值),但不能依赖它们,因为其他操作可能随时将该位置用于任何目的。
在我的看法中,您不太可能看到* i 抛出段错误 - 尽管对于使用指针的更复杂类型可能会发生这种情况,但您可能会看到它具有新值。
其他集合可能比列表具有更可预测的行为。 IIrc,向量将紧缩存储区,因此只有在罕见情况下进一步解引用才能看到以前的值。


是的,我知道如何使用它(i = l.erase(i);),我只是在想。 - Kiril Kirov
@kiril kirov:我从你的问题中理解了这一点,但是我认为如果提供这些信息,答案会更加完整。希望剩下的内容有助于揭示这种行为的神秘面纱。 - PypeBros

1

看起来迭代器仍然指向这个内存....
如果你写了些东西到这个块中,
也许下一次 *i 就会抛出分段错误。

虽然只是猜测,抱歉。


1

由于您正在处理链表,列表的元素不需要在内存中紧密相邻。如果您尝试使用向量进行相同的操作,您可能(因为行为未定义)会遇到以下问题:

cout << *i

将被打印为2。

然而,这并不是一种非常安全的编程方式。因此,一旦你擦除了一个迭代器,请确保不再使用它,除非你再次用begin()或end()等初始化它。


1

尝试使用正确的选项编译您的代码,使用好的编译器,然后运行它。(对于VC++,/D_DEBUG /EHs /MDd似乎就足够了。对于g++,至少需要-D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC。两个编译器通常需要更多的选项。)它应该会崩溃。(当我尝试时确实如此。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接