为什么会打印以下内容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
返回,但我想知道为什么会这样?还是未定义,还是取决于编译器?
是的,这是未定义的行为。您正在取消引用一种野指针。您不应该使用i
after的值erase
。
是的,erase
破坏了指向的对象。但是,对于 POD 类型,破坏不会做任何事情。
erase
没有为被擦除的迭代器分配任何特殊的“null”值,迭代器不再有效。
“销毁”一个对象意味着它的内存被回收并且它的内容可能会被改变(主要是如果手写的析构函数这样做,并且可能是由于存储了与空闲内存相关的东西)。list::erase 返回一个新的迭代器,你应该使用它而不是作为参数传递的那个(我很想i = l.erase(i);
养成一个习惯)。
破坏绝不意味着记忆被扫除、抹去。从 CPU 的角度来看,以前有效的位置在大多数情况下仍然有效(即它可以获取值),但不能依赖,因为其他操作可能随时出于任何目的回收该位置。
您不太可能看到*i
抛出段错误,恕我直言 - 尽管使用指针的更复杂类型可能会发生这种情况,但您可能会看到它具有新值。
其他集合可能比列表具有更可预见的行为。IIrc,向量将压缩存储区域,因此只有在极少数情况下进一步取消引用才能看到先前的值i
。
似乎迭代器仍然指向这个内存......
如果你想在这个块上写点东西,
也许下次 *i 会抛出分段错误......
对不起,虽然猜测
由于您正在处理链接列表,因此列表的元素不必在内存中彼此“落后”。如果你想用一个向量尝试同样的事情,你可能会(因为行为是未定义的)体验
cout << *i
打印为 2。
但是,这不是一种非常安全的编程方式。所以一旦你删除了一个迭代器,请确保不要再次使用它,除非你用 begin() 或 end() 等再次初始化它。
尝试使用正确的选项编译代码,使用好的编译器,然后运行它。(对于 VC++,/D_DEBUG /EHs /MDd
似乎就足够了。对于 g++,-D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG
-D_GLIBCXX_DEBUG_PEDANTIC
至少。两个编译器都需要更多的选项。)它应该崩溃。(当我尝试时它确实如此。)