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返回,但我想知道为什么会这样?还是未定义,还是取决于编译器?

4

5 回答 5

15

是的,这是未定义的行为。您正在取消引用一种野指针。您不应该使用iafter的值erase

是的,erase 破坏了指向的对象。但是,对于 POD 类型,破坏不会做任何事情。

erase没有为被擦除的迭代器分配任何特殊的“null”值,迭代器不再有效。

于 2011-03-11T14:20:28.980 回答
2

“销毁”一个对象意味着它的内存被回收并且它的内容可能会被改变(主要是如果手写的析构函数这样做,并且可能是由于存储了与空闲内存相关的东西)。list::erase 返回一个新的迭代器,你应该使用它而不是作为参数传递的那个(我很想i = l.erase(i);养成一个习惯)。

破坏绝不意味着记忆被扫除、抹去。从 CPU 的角度来看,以前有效的位置在大多数情况下仍然有效(即它可以获取值),但不能依赖,因为其他操作可能随时出于任何目的回收该位置。

您不太可能看到*i抛出段错误,恕我直言 - 尽管使用指针的更复杂类型可能会发生这种情况,但您可能会看到它具有新值。

其他集合可能比列表具有更可预见的行为。IIrc,向量将压缩存储区域,因此只有在极少数情况下进一步取消引用才能看到先前的值i

于 2011-03-11T14:28:34.467 回答
1

似乎迭代器仍然指向这个内存......
如果你想在这个块上写点东西,
也许下次 *i 会抛出分段错误......

对不起,虽然猜测

于 2011-03-11T14:23:54.553 回答
1

由于您正在处理链接列表,因此列表的元素不必在内存中彼此“落后”。如果你想用一个向量尝试同样的事情,你可能(因为行为是未定义的)体验

cout << *i

打印为 2。

但是,这不是一种非常安全的编程方式。所以一旦你删除了一个迭代器,请确保不要再次使用它,除非你用 begin() 或 end() 等再次初始化它。

于 2011-03-11T14:33:29.580 回答
1

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

于 2011-03-11T18:04:14.810 回答