C++中前置++和后置++的坑(一不小心很容易造成操作不合法内存的错误!)

这是这我阅读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方法才不至于发生操作不合法内存的错误的发生!

发布了23 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/TanJiaLiang_/article/details/86692813