原文地址:http://www.studa.net/yingyong/080730/14502913-2.html
在map遍历的过程中,完成对符合条件结点的删除操作(这个是由map本身数据结构特性保证的)。在遍历的过程中最主要的就是怎么保证删除的结点在删除前将指针指向下一个结点(这一点正是我们要做的),在删除了当前结点后,map中的数据结构能够保证后续的迭代器指针是有效的,而且后续的结点都没有遍历过(这个特性是由map底层的红黑树的相关操作保证的)。所以需要将迭代器指向下一个结点后再删除当前符合条件的结点。
#pragma warning(disable:4786)
#include <map>
#include <string>
#include <iostream>
using namespace std;
void main(){
map<int, string> mapStudent;
mapStudent.insert(map<int, string>::value_type(1, "one"));
mapStudent.insert(map<int, string>::value_type(2, "two"));
mapStudent.insert(map<int, string>::value_type(3, "three"));
map<int, string>::iterator iter;
string aa = "three";
iter = mapStudent.begin();
for(; iter != mapStudent.end(); )
{
if((iter->second) >= aa)
{
//满足删除条件,删除当前结点,并指向下面一个结点
mapStudent.erase(iter++);
}
else
{
//条件不满足,指向下面一个结点
iter++;
}
}
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
cout<<iter->first<<" "<<iter->second<<endl;
}
}
这种删除方式也是STL源码一书中推荐的方式。比较一下mapStudent.erase(iter++);和mapStudent.erase(iter);iter++;这个执行序列。
不妨做个简单的测试,看看汇编执行下的执行序列:
void func(int a)
{}
int main(int, char**)
{
int iPos = 0;
func(iPos++);
}
函数调用func(iPos++)执行序列:将iPos放入寄存器edx中(缓存起来),随即对iPos做加操作 inc dword ptr [ebp-0x04]。也就是说函数调用中的iPos++的执行时期在函数体func执行前就已经完成,而函数体中的参数使用的是iPos未做加操作之前的副本。再分析mapStudent.erase(iter++)语句,map中在删除iter的时候,先将iter做缓存,然后执行iter++使之指向下一个结点,再进入erase函数体中执行删除操作,删除时使用的iter就是缓存下来的iter(也就是当前iter(做了加操作之后的iter)所指向结点的上一个结点)。
根据以上分析,可以看出mapStudent.erase(iter++);和map Student.erase(iter);iter++;这个执行序列是不相同的。前者在erase执行前进行了加操作,在iter被删除(失效)前进行了加操作,是安全的;后者是在erase执行后才进行加操作,而此时iter已经被删除(当前的迭代器已经失效了),对一个已经失效的迭代器进行加操作,行为是不可预期的,这种写法势必会导致map操作的失败并引起进程的异常。
附:
当使用STL map的void erase( iterator pos )时不能传入值为end()的pos,否则可能对容器造成破坏,导致严重错误。