在vector的循环中调用erase

来源:互联网 发布:mysql时间戳类型长度 编辑:程序博客网 时间:2024/05/19 00:40

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

然而我们不能依赖于这个一般性事实,而应该采用it=intVec.erase(it)的形式来对it重新赋值。STL中有要求vector的erase函数要返回指向被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/。

 

最初犯了这样的错误:

for(vector<TGestureInfo>::iterator it=m_stGestureInfo.begin(); it != m_stGestureInfo.end(); /*it++*/)
{
pCurrTraj = pRoot;
bool bSameFlag = false;
while (pCurrTraj!=NULL)

int iTrajID = pCurrTraj->GetID();

if((*it).iTrajID == iTrajID)
{
bSameFlag = true;
break;
}

pCurrTraj= pCurrTraj->GetNext(); 
}

if(false == bSameFlag)
it = m_stGestureInfo.erase(it);

if(it == m_stGestureInfo.end()) //要控制迭代器不能超过整个容器 

break; 

}

 

这样导致,每次删除后,删除的后一个元素不参与判断。因为erase之后,后面的元素会移位,此时it指向另外删除后面的元素,然后it++,最后会导致删除后面的元素被跳过,因此

正确写法:

for(vector<TGestureInfo>::iterator it=m_stGestureInfo.begin(); it != m_stGestureInfo.end(); /*it++*/)
 {
  pCurrTraj = pRoot;
  bool bSameFlag = false;
  while (pCurrTraj!=NULL)
  {   
   int iTrajID = pCurrTraj->GetID();

   if((*it).iTrajID == iTrajID)
   {
    bSameFlag = true;
    break;
   }

   pCurrTraj= pCurrTraj->GetNext();   
  }

  if(false == bSameFlag)
   it = m_stGestureInfo.erase(it);
  else
   it++;

  } 

 

0 0
原创粉丝点击