题目

编写程序,演示在遍历 std::vector 容器的过程中删除所有偶数元素时最容易踩入的“迭代器失效”(Iterator Invalidation)死循环/越界崩溃陷阱;分别给出基于 it = vec.erase(it) 返回值步进的安全传统修正方案,以及现代 C++ 标准推荐的 std::remove_if 与 C++20 统一 std::erase_if 的彻底删除写法。

输入格式:无需外部输入。

输出格式:输出删除偶数前后容器内元素序列与正确迭代过程。

数据范围与约定:STL 迭代器生命周期与安全修改。

思路与算法

1. 迭代器失效的成因
对 std::vector 执行 erase(it) 会导致被删元素之后的所有元素向前平移拷贝一位。此时原本的迭代器 it 已经失效;若循环中仍然无脑执行 ++it,不仅会漏检紧随其后的下一个元素,更可能越过 vec.end() 触发段错误(Segmentation fault)。

2. erase 返回值步进模式
标准规定 vec.erase(it) 会返回指向“被删除元素之后那个新元素”的合法迭代器。因此正确写法是在删除时不执行 ++it,仅在不删除时才 ++it。

3. 现代 Erase-Remove 惯用法
手写循环删除的时间复杂度为 O(K * N);而使用标准算法 vec.erase(std::remove_if(...), vec.end()) 为线性 O(N),无额外冗余移动。

完整程序

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // 1. 传统迭代器循环安全删除(删除所有偶数)
    std::vector<int> vec1 = {1, 2, 2, 3, 4, 5, 6, 6, 7};
    std::cout << "--- 1. 正确的 it = vec.erase(it) 迭代模式 ---\n";
    std::cout << "原数组: ";
    for (int x : vec1) std::cout << x << " ";
    std::cout << "\n";

    for (auto it = vec1.begin(); it != vec1.end(); /* 内部步进 */) {
        if (*it % 2 == 0) {
            // erase 返回被删除元素后面的有效迭代器
            it = vec1.erase(it);
        } else {
            ++it; // 只有不删除时才向后步进
        }
    }

    std::cout << "删除所有偶数后: ";
    for (int x : vec1) std::cout << x << " ";
    std::cout << "\n\n";

    // 2. 现代 C++ 规范:Erase-Remove Idiom
    std::vector<int> vec2 = {10, 15, 20, 25, 30, 35};
    std::cout << "--- 2. 高效算法 erase + remove_if ---\n";
    vec2.erase(
        std::remove_if(vec2.begin(), vec2.end(), [](int n) { return n % 2 == 0; }),
        vec2.end()
    );

    std::cout << "Erase-Remove 删除后: ";
    for (int x : vec2) std::cout << x << " ";
    std::cout << "\n";

    return 0;
}

运行示例

输出:

--- 1. 正确的 it = vec.erase(it) 迭代模式 ---
原数组: 1 2 2 3 4 5 6 6 7 
删除所有偶数后: 1 3 5 7 

--- 2. 高效算法 erase + remove_if ---
Erase-Remove 删除后: 15 25 35