容器list使用之erase
今天程序里面使用了list容器,其中用到了erase()函数,程序竟然崩溃了。
之前一直没出现问题,感觉莫名奇妙。花了点时间,搞清楚了erase()函数的机理。
常用的删除容器中元素的方法是如下:
方法1:
1 2 3 4 5 6 7 8 9 10 11 12 13
| list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { iter = List.erase( iter ); } else { iter++; } }
|
方法2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { List.erase( iter++ ); } else { iter++; } }
|
有一种错误的写法(注意同方法2比较)
1 2 3 4 5 6 7 8 9 10 11
| list< int> List; list< int>::iterator iter; for( iter = List.begin(); iter != List.end(); ) { if(1) { List.erase( iter ); } iter++; }
|
我们看一下erase()函数的源代码(仅列出release下的代码):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| iterator erase(iterator _Where) { _Nodeptr _Pnode = (_Where++)._Mynode();
if (_Pnode != _Myhead) { _Nextnode(_Prevnode(_Pnode)) = _Nextnode(_Pnode); _Prevnode(_Nextnode(_Pnode)) = _Prevnode(_Pnode); this->_Alnod.destroy(_Pnode); this->_Alnod.deallocate(_Pnode, 1); --_Mysize; } return (_Where); }
|
函数在返回的时候,是返回当前迭代器的下一个节点。
所以当 iter = List.erase( iter ); 执行以后,迭代器自动指向了下一个元素。
而对于入参中的iter,所指的地址已经被销毁,所以写的时候,应该注意加上前面的iter =,并且是不需要再进行iter++操作。
那另外的一种写法,List.erase( iter++ ); 为什么也是对的呢?
这里研究了一下,这里需要讲一下++运算符的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| _Myt_iter& operator++() { ++(*(_Mybase_iter *)this); return (*this); }
_Myt_iter operator++(int) { _Myt_iter _Tmp = *this; ++*this; return (_Tmp); }
|
++实际上可以看做是一个函数。
对于++在后的情况(例如i++),函数在运行的时候,将运算的数据i已经改变,但是函数的返回值是操作之前的数据,所以在我们看来,i++好像是先进行了i的读取,才+1。
回到迭代器,List.erase( iter++ );就没有问题了。
对于那种错误的方法,List.erase( iter );在执行以后,iter所指的对象已经被销毁,所以再对iter进行操作是非法的,程序会出错。