STL 容器迭代器坑

erase() 函数

不论是使用关联式容器还是序列式容器,使用 iterator erase(const_iterator __position) 成员函数时应注意写法。因为一旦 erase 掉,该迭代器就会失效,不能继续进行操作。下面示范了错误样例:

map<int,int> MyMap;
//插入操作
for(map<int,int>::iterator it=MyMap.begin();it!=MyMap.end();it++)
	if(it->second%2) MyMap.erase(it);

直接会 RE。有两种改法:
1.

//method one
for(map<int,int>::iterator it=MyMap.begin();it!=MyMap.end();it++)
	if(it->second%2) it=MyMap.erase(it);

这种改法基于 erase() 函数的返回值——返回被删除的下一个迭代器。仅 c++11 以上可用。c++11 以下无返回值。定义如下(这个是 map 的,其它的也差不多):

#if __cplusplus >= 201103L
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // DR 130. Associative erase should return an iterator.
      /**
       *  @brief Erases an element from a %map.
       *  @param  __position  An iterator pointing to the element to be erased.
       *  @return An iterator pointing to the element immediately following
       *          @a position prior to the element being erased. If no such 
       *          element exists, end() is returned.
       *
       *  This function erases an element, pointed to by the given
       *  iterator, from a %map.  Note that this function only erases
       *  the element, and that if the element is itself a pointer,
       *  the pointed-to memory is not touched in any way.  Managing
       *  the pointer is the user's responsibility.
       */
	//这段话是说:删除传入的迭代器对应的元素(但如果元素是指针就什么都不会做。管理指针是用户的职责),返回被删除元素的下一个元素的迭代器。
      iterator
      erase(const_iterator __position)
      { return _M_t.erase(__position); }

      // LWG 2059
      _GLIBCXX_ABI_TAG_CXX11//这个是来判断兼容哪个版本的
      iterator
      erase(iterator __position)
      { return _M_t.erase(__position); }
#else
      /**
       *  @brief Erases an element from a %map.
       *  @param  __position  An iterator pointing to the element to be erased.
       *
       *  This function erases an element, pointed to by the given
       *  iterator, from a %map.  Note that this function only erases
       *  the element, and that if the element is itself a pointer,
       *  the pointed-to memory is not touched in any way.  Managing
       *  the pointer is the user's responsibility.
       */
      void//如果版本低了就没有返回值
      erase(iterator __position)
      { _M_t.erase(__position); }
#endif

2.

//method two
for(map<int,int>::iterator it=MyMap.begin();it!=MyMap.end();it++)
	if(it->second%2) MyMap.erase(it++);

这种改法新老版本都可以。

范围删除

在研究的时候发现另一个成员函数 iterator erase(const_iterator __first, const_iterator __last),可以范围删除元素。

迭代器的访问

对于序列式容器,我们可以直接修改迭代器对应元素。比如:

vector<int>vec({1,2,3,4,5});
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)printf("%d ",*it);
(*--it)=1;putchar('\n');
for(it=vec.begin();it!=vec.end();it++)printf("%d ",*it);

输出是:

1 2 3 4 5
1 2 3 4 1

但是对于关联式容器,这是不合法的。比如:

map<int,int>mp;
mp[1]=2,mp[2]=3;
map<int,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++)printf("%d %d\n",it->first,it->second);
(--it)->first=3;putchar('\n');
for(it=mp.begin();it!=mp.end();it++)printf("%d %d\n",it->first,it->second);

编译器会报错,它会说 (--it)->first 是 read-only member。这很好理解:关联式容器是基于一定规律的,如果修改了就会打破规律!真要修改,我们只能先把这个元素 erase 掉再插入一个进去。比如:

map<int,int>mp;
mp[1]=2,mp[2]=3;
map<int,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++)printf("%d %d\n",it->first,it->second);
mp.erase(--it);mp.insert(make_pair(3,3));
putchar('\n');
for(it=mp.begin();it!=mp.end();it++)printf("%d %d\n",it->first,it->second);

这个在 map 上表现不明显,但是在 set 的情形下很有用。当然也有特殊情形:map 的第二关键字可以改,它不是 read-only member。

posted @   robinyqc  阅读(53)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示