这是这我阅读STL之后自己实现list函数模板的时候发现的一个坑
STL里面有一个erase删除函数
函数原型是
iterator erase(iterator _P) //删除节点
{
_Nodeptr _S = (_P++)._Mynode();
_Acc::_Next(_Acc::_Prev(_S)) = _Acc::_Next(_S);
_Acc::_Prev(_Acc::_Next(_S)) = _Acc::_Prev(_S);
free(_S);
--_Size;
return (_P);
}
我们重载了erase的另外一个方法,里面调用的是iterator erase(iterator _P)这个函数原型
iterator erase(iterator _F, iterator _L)
{
//for (; _F != _L; ++_F) //这里有个坑,先删除再++
//{ //我们会发现删除之后_F所指向的空间已经被free了
// erase(_F); //我们++_F必然会造成内存的错误
//}
while (_F != _L) { //这里后置++,先算_F++
erase(_F++); //然后将_F++的返回值erase(_F++的返回值)
} //_F已经++其实已经++了
return _F;
}
for版本的实现和while版本的实现似乎并没有任何的区别
但是却暗藏玄机
for版本是在删除了节点之后,迭代器_F自动++,这就涉及到了一个逻辑的问题,我们都删了节点,而迭代器_F里面的指针指向的是已经删除(free掉)了的节点,我们还对_F进行++的操作,必然造成内存的错误!
而while版本里面调用的却是
erase(_F++);
这里面调用似乎好像很普通,但是里面蕴含了相当巧妙的方法。我们先看前置++和后置++的实现
iterator operator++() //++it
{
const_iterator::_Ptr = _Acc::_Next(const_iterator::_Ptr);
return *this;
}
iterator operator++(int) //it++
{
iterator _Tmp = *this;
++*this; //这里只能++*this不能*this++,因为后置++方法并没有完全实现好,必然会递归调用,无穷无尽
return _Tmp;
}
很明显后置++是通过一个临时的变量返回了进行++操作之前的*this,而我们的真正*this已经进行++操作了。
所以erase(_F++);中,我们的_F本身,已经进行++操作走人了,而erase函数调用的是erase(_F进行++之前的一个函数原型)
所以我们while方法才不至于发生操作不合法内存的错误的发生!