在vector的循环中调用erase

    在使用vector的过程中,有时会遇到需要循环遍历vector,并删除符合指定条件的元素。

    当“指定条件”不复杂时,应该尽量使用erase(remove_if(begin, end, func), end)的形式来完成功能。

    但有时候“指定条件”过于复杂,不得不显式地写一个for循环来处理。我们必须小心在意erase所带来的side effect,一个一般性的for循环如下:

 1 for (std::vector<int>::iterator it = intVec.begin(); it != intVec.end(); /**/)
 2     {
 3         if (*it == 3)
 4         {
 5             intVec.erase(it);
 6         }
 7         else
 8         {
 9             ++it;
10         }
11     }

    所要注意的是it = intVec.erase(it)。实际上这里如果写成intVec.erase(it),即不对it做重新赋值,代码也能正常执行,特别是release版本几乎所有的编译器编译后都能产生结果正确的代码。而debug模式下有一些较新的编译器会在编译时给出警告,并在运行时出现断言错误。

    为什么一个错误的写法在大多数情况下都能得到正确的答案?

    根据STL的描述,执行erase(it)后,itit之后的迭代器都可能会失效。这一点很好理解。因为vector一般由动态数组实现,它的元素在内存中是连续存储的。当删除掉it所指向元素时,原本在it后面的元素需要集体前移。迭代器本身几乎可以理解为是一个指针,在erase之后它所指向的位置并没有变化,只是那个位置的元素发生了变化,而且恰好变成了我们所想要的。至少大多数STL版本是这么实现的,因为这处理起来比较自然。

    然而我们不能依赖于这个一般性事实,而应该采用it=intVec.erase(it)的形式来对it重新赋值。STL中有要求vectorerase函数要返回指向被erase的迭代器的下一个位置,写成it=intVec.erase(it)是万无一失的,而写成intVec.erase(it)虽然实际可行,但是具有潜在风险,万一某一天erase会影响it的指向(STL只要求erase移除元素,而没有保证it自身不变),程序就极有可能出问题。

    根据标准所描述的约束来编程,而不是根据具体的实现细节来编程。

    对于C++ STL,似乎有很多个版本的实现,而它们或多或少都有所偏差。这里有两个网站,可以进行参考:http://www.cplusplus.com/reference/http://www.sgi.com/tech/stl/

posted @ 2012-04-19 22:12  carter2000  阅读(6836)  评论(0编辑  收藏  举报