STL容器迭代器失效浅析

迭代器失效一般发生在对容器进行插入及删除操作时,插入/删除操作可能导致空间的重配置以及所指对象的位移而带来迭代器失效问题,我们可以归纳为以下两点:

  1. 由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。
  2. 由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。

容器插入删除操作的迭代器情况

一般关联式容器的删除操作都会造成迭代器的失效下面通过几个序列容器的例子来简要分析以下迭代器失效的情况。

vector

vector是一个可以动态扩充的数组,它与array非常相似,不过随着新元素的加入它的内部机制会自行扩充空间以容纳新元素。
这里写图片描述
对于insert操作,若引起空间的重新分配即插入的元素无法被已有的容器容纳而造成的空间重分配时,原来的迭代器会指向无效的地址空间从而造成迭代器的失效。
对于erase操作,由于删除元素而带来的元素向前迁移,会导致当前迭代器及以后所有的迭代器失效。

list

list是一个双向链表,插入和删除都能在O(1)时间内完成,不会造成空间的重新配置或是数据的大规模迁移。
这里写图片描述
对于insert操作,所有迭代器均不会失效。
对于erase操作,当前迭代器会由于节点的删除而失效,其他迭代器均不会失效。

deque

deque是一种双向开口的连续线性空间,它允许常数时间内对头端进行元素的插入或删除操作。他不想vector那样占用一大片连续的线性空间,它是动态的以分段连续空间组合而成。
这里写图片描述
deque的迭代器相比上面两种是相对复杂的它首先应能够指出当前所在节点,以及所在节点中缓冲区的位置。
这里写图片描述
对于insert操作,deque的插入操作有push_back/push_front/insert增加任何元素都将使deque的迭代器失效,从STL的源码中可以看出,无论是哪种方式的插入操作,迭代器都会被调整从而导致迭代器失效。
对于erase操作,在deque的中间删除元素将使迭代器失效。在deque的头或尾删除元素时,只有指向该元素的迭代器失效。在删除两端的元素时,由于删除完毕后迭代器的调整,迭代器不会失效,而若删除中间节点的元素,当前的迭代器会失效。

### STL `erase` 方法导致迭代器失效的问题 当使用 C++ 标准模板库 (STL) 的容器成员函数 `erase` 删除元素时,可能会使某些迭代器失效。这取决于所使用的具体容器类型。 对于 **序列式容器** 如 `std::vector`, 当执行 `erase` 操作移除某个位置上的元素之后,在该位置及其后的所有迭代器都会被视作已失效[^5]。这意味着如果尝试继续使用这些旧有的迭代器来访问或修改容器内的其他项,则可能导致未定义行为。 而对于像 `std::list` 或者双向链表这样的双端开口列表来说,只有指向被删节点本身的那个特定迭代器会被标记为无效;其余部分保持有效状态不变。 至于 **关联式容器**, 例如 `std::map` 和 `std::set`, 它们内部实现通常基于平衡树结构。因此在这类集合上调用 `erase` 函数仅会使对应键值对映射关系断开连接,并不会影响到任何现存的有效迭代器指针。 #### 解决方案 为了安全地遍历并可能从中删除项目而不引起迭代器失效问题: - 使用返回新合法迭代器的方法:许多容器提供了一个版本的 `erase` ,它接受单个参数(即要擦除的位置),并且返回紧跟其后的下一个可用位置的一个迭代器。这样可以在循环体内直接更新当前迭代器变量。 ```cpp for(auto it = vec.begin(); it != vec.end();) { if(/* condition */) { it = vec.erase(it); } else { ++it; } } ``` - 利用范围形式的 `erase`: 如果可以预先知道哪些区间内全部都需要清除掉的话,那么可以直接指定起始结束边界来进行批量处理操作,从而减少不必要的多次单独调用所带来的性能损耗以及潜在风险。 ```cpp auto start_it = find_if(vec.begin(), vec.end(), /* predicate */); auto end_it = remove_if(start_it, vec.end(), /* another_predicate */); vec.erase(start_it, end_it); ``` - 针对支持随机存取特性的容器如数组向量等特殊情形下还可以考虑采用先交换再弹出的方式完成目标对象清理工作,此做法能够有效地规避因连续内存布局特性引发的一系列连锁反应式的迭代器破坏现象发生。 ```cpp if (!vec.empty() && should_remove_last_element()) { std::swap(*find_position_to_erase, vec.back()); vec.pop_back(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值