浅谈c++ stl迭代器失效的问题

2020-01-06 16:10:19于丽

之前看《C++ Primier》的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究。今天写程序的时候遇到了这个问题。

1 莫名其妙的Erase

最初我的程序是酱紫的,别说话,我知道这样是有问题的,可这样是最直观的想法


int arr[]={0,1,2,3,4,5,6,7,8,9,10}; 
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));for (auto it = a.begin(); it != a.end();++it ){
    if ((*it)&1){
      a.erase(it);
    }
  }  

没错,程序崩溃!删除了迭代器it之后,it迭代器失效了,无法再进行++it操作了。

可是,当我觉得erase做的只是把it之后的元素向前移动一个位置而已,为什么迭代器失效了呢?我翻开《STL源码剖析》,SGI STL的vector<T,Alloc>::erase的源码是这样的:


iterator vector<T, Alloc>::erase(iterator position)
  {
    if (position + 1 != end())
      copy(position + 1, finish, position);
    --finish;
    destroy(finish);
    return position;
  }

 正如我所想,erase函数并没有对输入的position迭代器进行改写!我打印出调试信息,发现erase之后,迭代器的_Ptr成员,也就是指针的值并没有发生变化,而此指针所指的元素的确是下一个元素。那么为什么失效了呢?

我又查了《C++ Primier》,发现此书上的标准写法是这样的:


int arr[]={0,1,2,3,4,5,6,7,8,9,10}; 
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));
  for (auto it = a.begin(); it != a.end();){
    if ((*it)&1){
      it=a.erase(it);
    }
    else
      ++it;  
  }

运行了一下,这样是没错的。我打印了调试信息,发现与之前一样,erase之后把结果赋给it,it里的成员_Ptr并没有发生变化。唯一的可能就是迭代器里还有别的标志,如果当前元素被删除之后,该迭代器也就“失效”了。《C++ Primier》并未对此作出过多解释,只是说,erase函数返回被删除元素的下一个元素的迭代器。

结论:在STL里,我们不能以指针来看待迭代器,指针是与内存绑定的,而迭代器是与容器里的元素绑定的,删除了之后,该迭代器就失效了,在对其重新赋值之前,不能再访问此迭代器。

2 更加小心冀冀地Insert

机智如我,自然会去探索一下insert之后,迭代器会怎样。于是: 


vector<int> a;
  for (int i = 0; i < 10; ++i)
  {
    a.push_back(i);
  }

  for (auto it = a.begin(); it != a.end(); ++it){
    if (*it == 5){ 
      a.insert(it, 100);
       ++it;
    }
  }

你猜怎么着??

啥事儿没有!你可能会问,插入之后为什么要++it。插入之前,it指向5,在5之前插入100后,it指向100。这样下一次循环,it依然会指向5。相信我,你的程序会爆炸的!