C++迭代器失效

news2024/12/19 9:10:16

        在STL中,有些操作会导致迭代器失效,即之前获取的迭代器无法再安全地使用。这是因为这些操作可能会改变容器的结构,例如插入、删除元素等。

        具体来说,以下情况下迭代器会失效:

1. 当插入或删除元素导致容器中的内存重新分配时,所有指向容器元素的迭代器都会失效。这是因为容器重新分配了内存空间,原来的迭代器指向的内存已经无效。
2. 当插入元素导致迭代器位置被移动时,被移动的迭代器和之后的所有迭代器都会失效。这是因为插入元素后,容器中的元素位置发生了改变。
3. 当删除元素导致迭代器位置被移动或失效时,被删除元素的迭代器和之后的所有迭代器都会失效。这是因为删除元素后,容器中的元素位置发生了改变或者被删除的元素迭代器已经不存在。
4. 当对容器进行排序操作时,迭代器的相对顺序可能发生变化,因此之前的迭代器可能会失效。

        为避免迭代器失效,可以采取以下策略:

1. 尽量避免在循环中进行插入和删除元素的操作。如果必须进行这些操作,可以使用插入和删除后返回新的迭代器,而不是继续使用之前的迭代器。
2. 在遍历容器时,尽量使用前向迭代器或双向迭代器,避免使用随机访问迭代器。因为前向迭代器和双向迭代器的失效范围更小,而随机访问迭代器的失效范围更大。
3. 在可能引起迭代器失效的操作之后,务必更新迭代器,保证它们仍然指向有效的位置。
4. 如果需要在迭代器失效的情况下继续操作容器,可以使用容器提供的索引值或其他标识来重新获取迭代器。

        总之,迭代器失效是在使用STL容器时需要注意的问题,了解容器操作可能引起迭代器失效的情况,能够帮助避免潜在的错误。在编写代码时,建议谨慎使用迭代器,并在操作容器后适时更新迭代器,以确保程序的正确性。

4.1 示例

4.1.1 插入操作导致的迭代器失效

<1> vector 容器 

std::vector<int> v{1, 2, 3, 4, 5};
auto it = v.begin();

v.insert(it + 2, 9); // 在迭代器位置之前插入元素

// 插入元素后,原来的迭代器`it`已经失效,需要重新获取迭代器
it = v.begin();

for (; it != v.end(); ++it) {
    std::cout << *it << " ";
}

输出结果:1 2 9 3 4 5

        在vector容器中,插入元素后,原来的迭代器会失效,因为插入元素后,可能会导致容器重新分配内存,迭代器指向的位置被移动。

<2> list容器 

std::list<int> lst{1, 2, 3, 4, 5};
auto it = lst.begin();

lst.insert(std::next(it, 2), 9); // 在迭代器位置之后插入元素

// 插入元素后,原来的迭代器`it`仍然有效
for (; it != lst.end(); ++it) {
    std::cout << *it << " ";
}

输出结果:1 2 3 9 4 5

        在list容器中,插入元素不会导致迭代器失效,因为list使用链表结构存储元素,插入元素后,原来的迭代器仍然保持有效。

 <3> map容器

std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
auto it = m.begin();

m.insert(std::make_pair(4, "four")); // 插入键值对

// 插入元素后,原来的迭代器`it`仍然有效
for (; it != m.end(); ++it) {
    std::cout << it->first << ":" << it->second << " ";
}

输出结果:1:one 2:two 3:three 4:four

map容器中,插入元素不会导致迭代器失效。

4.1.2 删除操作导致的迭代器失效

下面是一个示例代码,演示了使用vector容器删除元素操作导致迭代器失效的情况:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
    auto it = v.begin();
    while (it != v.end()) {
        if ((*it % 2) == 0) {
            std::cout << "erase " << *it << std::endl;
            v.erase(it); // 删除偶数元素
        }
        else {
            it++;
        }
    }
    return 0;
}

        这个程序会遍历vector容器v中的所有元素,如果某个元素是偶数,则删除它。然而,这个程序存在迭代器失效的问题,因为在删除元素后继续使用同一个迭代器进行遍历容器。事实上,运行这个程序会导致Segmentation fault错误,并且程序崩溃。

        这个程序的问题在于,在执行v.erase(it)操作后,迭代器it指向的位置已经变为了被删除元素的下一个位置,而不是原来的下一个位置。因此,在使用it进行下一次循环遍历时,会访问到已经被删除的元素,从而导致异常。

vector iterator not incrementable错误通常是因为在使用vector容器的迭代器时出现了问题。这个错误可能由以下几种情况引起:

  1. 在循环中对迭代器进行了无效的增量操作。比如,在循环中使用++运算符对迭代器进行递增,但在某些条件下,没有确保迭代器仍然有效。这可能是由于删除元素或修改容器结构导致的。

  2. 在使用迭代器之前,对vector容器进行了修改操作。如果在使用迭代器进行遍历之前,对vector容器执行了插入、删除或排序等改变容器大小的操作,那么迭代器可能会失效。

要解决这个错误,可以考虑以下几点:

(1)确保在对迭代器进行增量操作之前,先判断迭代器是否有效。可以使用条件语句(如if)或循环控制语句(如while)来判断迭代器是否指向有效的元素。

   要判断迭代器是否有效,可以通过比较迭代器与容器的begin()end()迭代器进行判断。如果迭代器位于容器范围内,则表示迭代器有效;否则,表示迭代器无效。

以下是几种常见的方法来判断迭代器的有效性:

<1> 使用相等运算符(==!=):通过将迭代器与容器的begin()end()迭代器进行比较来判断迭代器是否位于容器范围内。例如:

std::vector<int> v{1, 2, 3, 4, 5};
auto it = v.begin();

if (it != v.end()) {
    // 迭代器有效
} else {
    // 迭代器无效
}
 

<2> 使用std::distance()函数:std::distance()函数可以返回两个迭代器之间的距离。通过计算迭代器与容器的begin()end()迭代器之间的距离,可以判断迭代器是否在容器范围内。例如:

std::vector<int> v{1, 2, 3, 4, 5};
auto it = v.begin();

if (std::distance(it, v.end()) >= 0) {
    // 迭代器有效
} else {
    // 迭代器无效
}
 

<3> 使用异常处理:有些容器提供了在迭代器失效时抛出异常的机制。例如,std::list容器在删除迭代器指向的元素后,迭代器会失效,并抛出std::list::iterator类型的异常。可以捕获这个异常来判断迭代器是否有效。例如:

std::list<int> lst{1, 2, 3, 4, 5};
auto it = lst.begin();

try {
    // 在此处进行可能导致迭代器失效的操作
} catch (const std::list<int>::iterator& e) {
    // 迭代器无效
}
在使用迭代器时,注意避免对已经失效的迭代器进行操作,以免引发未定义行为。

(2)如果需要对vector容器进行修改操作,建议在遍历容器之前完成所有修改。

(3)如果需要在遍历过程中删除元素,可以使用正确的方式进行删除。可以使用返回新的迭代器的vector::erase()函数,并将其返回值赋值给迭代器,或者使用vector::erase()的重载版本,传递要删除的元素的位置范围。

为了解决这个问题,可以修改程序,使用返回新的迭代器的vector::erase()函数,如下所示:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
    auto it = v.begin();
    while (it != v.end()) {
        if ((*it % 2) == 0) {
            std::cout << "erase " << *it << std::endl;
            it = v.erase(it); // 删除偶数元素,并返回新的迭代器
        }
        else {
            it++;
        }
    }
    return 0;
}

 输出结果:

        这个程序与之前的程序类似,但是使用了返回新的迭代器的v.erase(it)函数,在删除元素后将其返回值赋值给迭代器it,以保证下一次循环遍历时,迭代器指向的位置仍然是有效的。运行这个程序,会得到正确的输出,没有异常发生。

持续更新!!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go并发可视化解释 - sync.WaitGroup

场景 Avito是一名校车司机&#xff0c;他帮助4个Gopher孩子上学。每天&#xff0c;Avito在他们的社区等待孩子们。他不知道孩子们需要多长时间&#xff0c;但他确切地知道有4个孩子他需要等待。 1*aZnEggopv4Tsbyyj3e5JFg.png 当一个孩子准备好时&#xff0c;他/她会说&#xf…

跨境干货 | 如何搭建自己的独立站?

很多跨境电商在第三方平台经营许久后&#xff0c;会想摆脱第三方规则的限制建立起自己的品牌。 对于新手来说建立独立站是很有挑战的&#xff0c;建立独立站前首先我们需要了解一下什么是独立站&#xff1f;从字面意思来理解就是拥有属于自己的网站自己搭建跨境独立站&#xff…

【Python语言速回顾】——基础语法

目录 引入 一、PEP8代码规范和风格 二、变量和数据 1、变量 2、运算符 三、三种程序结构 1、分支结构 2、循环结构 四、组合数据类型 1、列表&#xff08;list&#xff09; 2、元组&#xff08;tuple&#xff09; 3、字典&#xff08;dict&#xff09; 5、集合&…

Python学习基础笔记七十七——json序列化

客户端和服务端之间需要交换数据才能完成各种功能。 假设 服务端程序都是用Python语言开发的话&#xff0c;那么 服务端从数据库中获取的最近的交易列表&#xff0c;可能就是像下面这样的一个Python列表对象&#xff1a; historyTransactions [{time : 20170101070311, #…

LoadRunner录制脚本+编写脚本

LoadRunner安装* 为什么选择LoadRunner 1&#xff09;Jmeter没有录制功能 2&#xff09;可以设计非常非常丰富的测试场景 3&#xff09;LoadRunner能够产出非常丰富的测试报告 LoadRunner三大组件的关系 每个组件是干什么的 VUG&#xff1a;录制脚本&#xff0c;&#xff…

天梯赛:L1-005 考试座位号

每个 PAT 考生在参加考试时都会被分配两个座位号&#xff0c;一个是试机座位&#xff0c;一个是考试座位。正常情况下&#xff0c;考生在入场时先得到试机座位号码&#xff0c;入座进入试机状态后&#xff0c;系统会显示该考生的考试座位号码&#xff0c;考试时考生需要换到考试…

线程是如何进行创建的

对于任何一个进程来讲&#xff0c;即便我们没有主动去创建线程&#xff0c;进程也是默认有一个主线程的。线程是负责执行二进制指令的&#xff0c;它会根据项目执行计划书&#xff0c;一行一行执行下去。进程要比线程管的宽多了&#xff0c;除了执行指令之外&#xff0c;内存、…

局域网下多台windows电脑时间同步

windows时间同步 最近在项目中遇见了多台windows电脑的时间同步问题。在这个项目中&#xff0c;有五台电脑&#xff0c;五台电脑处于同一局域网下&#xff0c;其中有一台可以连接互联网&#xff08;A电脑&#xff09;。我需要将其他四台电脑&#xff08;B、C、D、E电脑&#xf…

在Word中,图片显示不全

在今天交作业的时候&#xff0c;发现了一个非常SB的事情&#xff0c;把图片复制过去显示不完全&#xff1a; 使用文心一言查看搜索了一下&#xff0c;发现可能是以下几种原因&#xff1a; 图片所在行的行高设置不正确。可以重新设置行高&#xff0c;具体步骤包括打开图片显示…

【Java基础面试三十八】、请介绍Java的异常接口

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;请介绍Java的异常接口 …

LabVIEW中PID控制的的高级功能

LabVIEW中PID控制的的高级功能 比例-积分-微分&#xff08;PID&#xff09;控制占当今控制和自动化应用的90%以上&#xff0c;主要是因为它是一种有效且简单的解决方案。虽然PID算法最初用于线性、时不变系统&#xff0c;但现在已经发展到控制具有复杂动力学的系统。在现实世界…

git_笔记

git 学习视频 【【狂神说Java】Git最新教程通俗易懂】 https://www.bilibili.com/video/BV1FE411P7B3/?p10&share_sourcecopy_web&vd_source271b4386fd06dbbfd6504cf65bb85f31 git的下载与安装 git官网&#xff1a; https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%…

MATLAB关于线性优化和整数线性优化

1.线性优化 x linprog(f,A,b,Aeq,beq,ub.lb) A&#xff0c;b是不等式约束Ax<b中的矩阵A与向量b&#xff0c; Aeq&#xff0c;beq是等式约束中的矩阵Aeq与向量beq&#xff0c; ub&#xff0c;lb是决策变量上下界 2.混合整数线性优化函数 x intlinprog(f,intcon,A,b,Aeq,beq…

【数据结构】面试OJ题——时间复杂度

目录 一&#xff1a;旋转数组 思路&#xff1a; 二&#xff1a;消失的数字 思路&#xff1a; 一&#xff1a;旋转数组 189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负…

模板(进阶)

文章目录 1.非类型模板参数1.1 定义&#xff1a;1.2 应用&#xff1a; 2.模板特化&#xff08;特化必须原模版&#xff09;2.1类模板特化2.2 函数模板特化 3模板的分离编译3.1 什么是分离编译3.2模板不能实现分离编译的原因&#xff08;简&#xff09;&#xff1a;3.3解决方法实…

VBA技术资料MF72:利用函数判断文件及工作表存在

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

初始c++(2)

目录 目录&#xff1a; 1. 引用 2. 内联函数inline 3.auto关键字(大概的认识) 4.基于范围的for循环(c11) 5.nullptr(c11) 本章最重要的知识点其实是前两个(详细讲解),后面的大概只会介绍一下用法。 文章开始&#xff01;&#xff01;&#xff01;&#xff01; 1&#xff1a;引…

微信小程序授权登录介绍

目录 一. 小程序登录如何获取微信用户信息 二. 小程序微信授权登录示例 后台代码 小程序代码 效果展示 三. 微信emoji存储问题 一. 小程序登录如何获取微信用户信息 小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识&#xff0c;快速建立小程序内…

【ArcGIS模型构建器】01:模型构建器Model Builder介绍

文章目录 一、 空间建模概述1. 空间建模概述2. 空间建模步骤 二、Model Builder建模1. Model Builder基础2. Model Builder操作3. Model Builder高级进阶操作 一、 空间建模概述 1. 空间建模概述 空间建模的目的是解决与地理有关的问题&#xff0c;通常涉及多种空间分析操作的…

100097. 合法分组的最少组数

给你一个长度为 n 下标从 0 开始的整数数组 nums 。 我们想将下标进行分组&#xff0c;使得 [0, n - 1] 内所有下标 i 都 恰好 被分到其中一组。 如果以下条件成立&#xff0c;我们说这个分组方案是合法的&#xff1a; 对于每个组 g &#xff0c;同一组内所有下标在 nums 中…