stl vector遍历删除,你真的会吗?

博客指出网上常见的vector遍历删除代码存在问题,改动后并非所有指定元素都被删除。原因是vector执行删除后成员前移,导致迭代器跳过元素。解决方法可去掉++,也可采用特定写法。还提到该代码在list上能正常工作,因list删除无成员移动问题。

今天我发现一个被骗好多年的事情,直接看代码

void remove() {
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    for(auto it=vec.begin();it!=vec.end();) {
        if(*it==1) {
            vec.erase(it++);
        } else {
            it++;
        }
    }
}

以上代码是网上常见的vector遍历删除的例子,貌似也可以工作,我也这么认为了好多年!现在我们稍加改动一下!

void remove() {
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(1);
    vec.push_back(2);
    for(auto it=vec.begin();it!=vec.end();) {
        if(*it==1) {
            vec.erase(it++);
        } else {
            it++;
        }
    }
}

你会发现这次有点不对,并不是所有的1都被删除了,为什么会这样呢?原因就是下面这句代码:

vec.erase(it++);

原来vector执行删除后,后面的成员会前移,也就是it并没有变,但是他已经指向下一个元素了。这样执行it++后,相当于it指向了下下个元素,也就是说把下一个1给跳过去了。那怎么解决呢?一般来说我们把++去掉就行了,但是这样美感不太好,所以我们需要这样写

void remove() {
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(1);
    vec.push_back(2);
    for(auto it=vec.begin();it!=vec.end();) {
        if(*it==1) {
            it=vec.erase(it);
        } else {
            it++;
        }
    }
}

回顾:

既然有问题,那这块深入人心的代码是怎么流传的呢?除了稍微隐蔽一点,最大的原因是,这段代码在list上是可以正常工作的,因为list删除时,不会产生成员移动的问题。

<think>首先,用户的问题是关于C++中遍历vector容器的方法和示例。我需要根据系统级指令来构建回答:使用中文、正确格式数学表达式(但这里可能不需要,因为是编程问题)、结构化回答、生成相关问题、添加引用标识。回顾用户查询:"我想遍历Vector容器的方法或示例请问C++Vector遍历方法示例"。这表明用户需要遍历C++std::vector的方法和代码示例。从提供的引用中:-引用[1]:讨论了遍历vector的方式,包括使用显式迭代器。-引用[2]:展示了多种遍历方法,包括while循环、for循环和for_each算法。我应该综合这些信息,提供清晰、逐步的解释和代码示例。回答结构:1.**引言**:简要介绍vector遍历的重要性。2.**方法列表**:列出常见的遍历方法。3.**详细示例**:对每种方法提供代码示例。4.**总结**:比较方法或给出建议。5.**相关问题**:生成相关问题。6.**引用**:在相关段落末尾添加引用标识。确保回答真实可靠:基于标准C++实践,引用来源。现在,整理遍历方法:1.**使用索引和下标**:通过size()和[]运算符。2.**使用迭代器**:-显式迭代器(如std::vector<int>::iterator)。-范围for循环(C++11起)。3.**使用算法**:如std::for_each。从引用[2]中提取:-方法1:while循环withiterator.-方法2:for循环withiterator.-方法3:for_eachwithfunction.另外,添加范围for循环作为现代C++方式。代码示例需要正确:使用$...$forinlinecode?不,系统指令说行内数学表达式用$...$,但代码块应该用```...```格式,如示例所示。在示例中,代码块是用```python```标记的,但这里是C++,所以用```cpp```或类似。系统指令说:"所有行内数学表达式...必须使用$...$格式",但这是针对数学的。对于代码,示例使用了代码块。所以,我将使用代码块来展示代码。生成相关问题:基于主题,如优化、性能、其他容器等。引用标识:在段落末尾添加[^1]或[^2],如果信息来自引用。计划回答:-开头:解释vector遍历的重要性。-方法1:下标访问。-方法2:迭代器(显式)。-方法3:范围for循环(C++11)。-方法4:for_each算法。-每个方法有代码示例。-结束:简要总结。-相关问题:3个左右。确保中文回答。</think>以下是关于C++中遍历`std::vector`容器的方法和示例的详细说明。`std::vector`是C++标准模板库(STL)中的动态数组容器,遍历是其常见操作,用于访问和修改元素。我将逐步介绍多种遍历方法,每种方法都提供代码示例。这些方法基于标准C++实践(C++11及以上),确保高效和可靠。代码示例中,我使用了简洁的注释以帮助理解。 ### 遍历`std::vector`的常见方法 遍历`vector`的方法主要包括:使用下标索引、迭代器(包括显式迭代器和范围for循环)、以及STL算法。每种方法各有优缺点:下标索引简单直观;迭代器更灵活,支持泛型编程;范围for循环(C++11引入)简洁现代;STL算法如`for_each`可结合函数对象实现复杂逻辑。下面逐一解释并提供示例。 #### 1. **使用下标索引遍历** - **方法描述**:通过`size()`获取元素数量,然后用索引运算符`[]`访问每个元素。优点是简单易读,但需注意索引越界风险(C++中`vector`不自动检查越界)。 - **代码示例**: ```cpp #include <iostream> #include <vector> int main() { std::vector<int> v = {1, 2, 3, 4, 5}; for (size_t i = 0; i < v.size(); ++i) { std::cout << v[i] << " "; // 输出每个元素 } std::cout << std::endl; return 0; } ``` - **输出**:`1 2 3 4 5` #### 2. **使用显式迭代器遍历** - **方法描述**:迭代器是类似指针的对象,用于遍历容器。使用`begin()`和`end()`获取迭代器范围。这种方法灵活,支持所有STL容器,但语法稍复杂。可选用`const`迭代器(如`const_iterator`)来防止修改元素[^1]。 - **代码示例**: ```cpp #include <iostream> #include <vector> int main() { std::vector<int> v = {1, 2, 3, 4, 5}; // 显式迭代器遍历(for循环) for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) { std::cout << *it << " "; // 解引用迭代器访问元素 } std::cout << std::endl; return 0; } ``` - **输出**:`1 2 3 4 5` - **变体:使用while循环**(引用[2]中提到的第二种方式): ```cpp std::vector<int>::iterator it = v.begin(); while (it != v.end()) { std::cout << *it << " "; ++it; } ``` #### 3. **使用范围for循环遍历(C++11及以上)** - **方法描述**:这是现代C++推荐的方式,语法简洁,自动处理迭代器。编译器会将其转换为迭代器操作,避免手动管理。支持常量遍历(如`const auto&`)以提高性能。 - **代码示例**: ```cpp #include <iostream> #include <vector> int main() { std::vector<int> v = {1, 2, 3, 4, 5}; // 范围for循环 for (const auto& element : v) { std::cout << element << " "; // 直接访问元素 } std::cout << std::endl; return 0; } ``` - **输出**:`1 2 3 4 5` #### 4. **使用STL算法`std::for_each`遍历** - **方法描述**:结合`<algorithm>`头文件中的`for_each`函数,传入函数对象或lambda表达式。适合复杂操作(如累计计算),但需要额外定义函数,可读性可能较低[^2]。 - **代码示例**: ```cpp #include <iostream> #include <vector> #include <algorithm> // 定义打印函数(可替换为lambda) void printElement(int val) { std::cout << val << " "; } int main() { std::vector<int> v = {1, 2, 3, 4, 5}; // 使用for_each遍历 std::for_each(v.begin(), v.end(), printElement); std::cout << std::endl; return 0; } ``` - **输出**:`1 2 3 4 5` - **使用lambda表达式简化**(C++11起): ```cpp std::for_each(v.begin(), v.end(), [](int val) { std::cout << val << " "; }); ``` ### 总结与建议 - **方法比较**: - 简单场景:优先使用**范围for循环**(简洁高效)。 - 需要索引或修改元素:**下标索引**或**显式迭代器**。 - 函数式编程:**`for_each`** 结合lambda。 - **注意事项**:遍历时避免在循环中修改容器大小(如添加/删除元素),以防迭代器失效。所有方法时间复杂度为$O(n)$,其中$n$为元素数量。 - **性能提示**:对于大vector,使用`const`迭代器或范围for循环的引用(`const auto&`)减少拷贝开销[^1][^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值