C++中vector迭代器哪些情况下会失效?

开发 前端
为了避免由于迭代器失效引起的错误,我们在使用迭代器遍历vector时,要避免在可能使迭代器失效的操作前后更新迭代器,或者尽量减少对vector的修改操作直到遍历完成。

在C++编程中,std::vector是一个非常强大的容器,用于存储动态数组。然而,使用std::vector时需要注意的一个重要方面是其迭代器的行为。当vector的容量发生变化时(例如添加或删除元素),迭代器可能会失效,这可能导致程序错误。

迭代器失效的情况:

1、重新分配内存

当向vector添加新元素导致其容量不足时,vector会自动扩展以容纳更多的元素。这种情况下,所有指向旧内存区域的迭代器、指针和引用都会失效。

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin(); // 指向第一个元素


    // 添加一个元素可能触发重新分配
    vec.push_back(4);


    if (it != vec.end()) {
        std::cout << "Element: " << *it << std::endl;
    } else {
        std::cout << "Iterator is invalid." << std::endl;
    }


    return 0;
}

在这个例子中,如果push_back()操作触发了重新分配,则it迭代器将变得无效,尝试访问它会导致未定义行为。

2、删除元素

从vector中移除元素后,该位置之后的所有元素及其对应的迭代器都会受到影响。

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto it = vec.erase(vec.begin()); // 删除第一个元素


    while (it != vec.end()) {
        std::cout << *it << " ";
        ++it;
    }
    std::cout << std::endl;


    return 0;
}

这里,erase()返回的是下一个有效元素的迭代器,确保了循环的安全性。

3、使用 clear() 方法清空容器

当调用clear()时,所有元素都会被删除,但已分配的内存空间不会被释放。这意味着容器的容量保持不变,但所有指向原元素的迭代器、指针或引用都将变得无效。

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto it = vec.begin(); // 获取开始位置的迭代器


    std::cout << "Before clear: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;


    vec.clear(); // 清空向量


    std::cout << "After clear: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;


    if (it != vec.end()) {
        // 这里会输出错误信息,因为it已经失效
        std::cout << "Element: " << *it << std::endl;
    } else {
        std::cout << "Iterator is invalid after calling clear()." << std::endl;
    }


    return 0;
}

调用clear()后,迭代器it仍然指向原来的内存位置,但由于这些位置上的元素已被删除,所以it实际上是无效的。为了安全起见,应该在clear()之后重新获取迭代器。

4、使用 resize() 改变大小

减小尺寸:如果通过resize()减小vector的尺寸,那么超出新尺寸范围的元素会被删除,相应的迭代器也会失效。

增大尺寸:如果增加尺寸,则新增加的位置上的迭代器是未定义的(直到它们被初始化)。

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto it = vec.begin() + 2; // 指向第三个元素


    std::cout << "Before resize: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
    std::cout << "Element at it before resize: " << *it << std::endl;


    vec.resize(2); // 减小尺寸到2


    std::cout << "After resizing to 2: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
    if (it != vec.end()) {
        // 这里可能会导致未定义行为,因为it已经失效
        std::cout << "Element at it after resize: " << *it << std::endl;
    } else {
        std::cout << "Iterator is invalid after resizing to 2." << std::endl;
    }


    vec.resize(6, 0); // 增大尺寸到6,并用0填充
    it = vec.begin() + 2; // 重新获取有效迭代器


    std::cout << "After resizing to 6: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
    std::cout << "Element at it after resizing to 6: " << *it << std::endl;


    return 0;
}

5、使用 swap() 交换两个 vector 的内容

当两个vector之间进行数据交换时,它们各自的迭代器、指针和引用都将指向对方的数据结构。这意味着原来的迭代器将不再指向原来的数据。

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6};


    auto it1 = vec1.begin();
    auto it2 = vec2.begin();


    std::cout << "Before swap: vec1[0] = " << *it1 << ", vec2[0] = " << *it2 << std::endl;


    vec1.swap(vec2); // 交换两个向量的内容


    std::cout << "After swap: vec1[0] = " << *it1 << ", vec2[0] = " << *it2 << std::endl;


    // 输出显示迭代器现在指向了交换后的数据
    std::cout << "it1 now points to: " << *it1 << std::endl;
    std::cout << "it2 now points to: " << *it2 << std::endl;


    return 0;
}

6、使用 assign() 重新赋值

assign()可以用来替换vector中的所有元素,这个过程类似于先调用clear()再插入新元素。因此,任何之前存在的迭代器都会失效。

#include <iostream>
#include <vector>


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


    std::cout << "Before assign: Element at it = " << *it << std::endl;


    vec.assign(3, 0); // 用3个0替换原有元素


    if (it != vec.end()) {
        // 这里会导致未定义行为,因为it已经失效
        std::cout << "After assign: Element at it = " << *it << std::endl;
    } else {
        std::cout << "Iterator is invalid after assign." << std::endl;
    }


    // 重新获取有效的迭代器
    it = vec.begin();
    std::cout << "New element at it: " << *it << std::endl;


    return 0;
}

vector删除经典错误

当从vector中删除一个元素时,所有指向该元素及其之后元素的迭代器都会失效。如果继续使用这些迭代器,会导致未定义行为。

下面这个是新手最容易犯的一个错误:

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};


    for (auto it = vec.begin(); it != vec.end(); ++it) {
        if (*it == 3) {
            vec.erase(it); // 删除元素3
        }
        std::cout << *it << " "; // 继续使用失效的迭代器
    }
    std::cout << std::endl;


    return 0;
}

当*it == 3时,调用vec.erase(it)会删除元素3。

erase()返回的是下一个有效元素的迭代器,但在这个例子中,it并没有更新为新的迭代器,而是继续递增,导致访问已失效的迭代器。

正确的做法是:

#include <iostream>
#include <vector>


int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};


    for (auto it = vec.begin(); it != vec.end(); ) {
        if (*it == 3) {
            it = vec.erase(it); // 更新迭代器
        } else {
            ++it; // 只有在没有删除元素的情况下才递增迭代器
        }
    }


    for (const auto &val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;


    return 0;
}

总结:

为了避免由于迭代器失效引起的错误,我们在使用迭代器遍历vector时,要避免在可能使迭代器失效的操作前后更新迭代器,或者尽量减少对vector的修改操作直到遍历完成。此外,使用基于范围的for循环(如for (auto &elem : vec))可以避免直接管理迭代器,从而简化代码并减少出错的机会。

责任编辑:武晓燕 来源: CppPlayer
相关推荐

2021-04-15 08:01:27

Spring声明式事务

2024-09-02 00:30:41

Go语言场景

2022-05-09 07:49:47

PulsarJava问题排查

2024-04-23 08:31:57

pythonfalse

2023-04-26 10:06:08

RocketMQ属性Consumer

2024-09-12 08:20:39

2024-02-19 08:01:59

服务微服务授权

2022-06-30 13:41:44

SQL 语句group by

2024-11-26 14:29:48

2015-06-01 06:39:18

JavaJava比C++

2015-06-29 09:06:51

2019-01-07 13:01:08

Linux惊叹用法命令

2022-11-07 17:23:03

2024-06-03 14:27:08

ThisAPIThat

2020-11-04 17:35:39

网络安全漏洞技术

2022-08-02 06:55:35

移动设备Android

2022-12-09 19:00:02

Vite兼容性BigInt

2023-07-04 08:48:24

静态代码分析工具

2015-06-29 14:23:13

JavaC++慢很多

2021-01-26 05:06:24

LinuxXargs 命令
点赞
收藏

51CTO技术栈公众号