STL容器详解:Vector高效使用指南

由于STL容器的相关接口使用太多了太多了,后续STL部分我就不采取像string那样分开详细举例子写了,只会在必要的地方给出代码例子,全汇总在一起,也方便后续学习回顾,毕竟我有强迫症,难受。。。。。。

目录

一、Vector(向量)容器

1、基本概念

2、存储特性

3、动态扩容机制

4、空间分配策略

5、操作效率分析

6、适用场景

补充说明:

二、Vector的定义方式

1、构造空容器

2、构造包含n个相同元素的容器

3、拷贝构造

4、使用迭代器范围构造

[5. 使用初始化列表构造(C++11及以上)](#5. 使用初始化列表构造(C++11及以上))

[6. 移动构造(C++11及以上)](#6. 移动构造(C++11及以上))

三、Vector容器的空间管理与大小

1、size与capacity的区别

2、reserve与resize函数

reserve函数

resize函数

3、empty函数

[解释 boolalpha(重要!!!)](#解释 boolalpha(重要!!!))

boolalpha

4、实际应用建议

5、增长机制(了解即可)

关于reserve和resize的区别:

VS运行结果(1.5倍扩容):

g++运行结果(2倍扩容):

四、Vector迭代器使用

1、迭代器概述

2、正向迭代器

begin()和end()

3、反向迭代器

rbegin()和rend()

4、常量迭代器

5、迭代器使用注意事项

6、实际应用示例

五、Vector的增删查改操作

1、尾部操作:push_back和pop_back

注意事项:

2、任意位置操作:insert和erase

时间复杂度分析:

3、查找操作:find函数

注意事项:

4、交换操作:swap

特点:

5、元素访问方式

下标访问

迭代器访问

范围for循环

访问方式比较:

总结

六、Vector迭代器失效问题

1、迭代器失效的概念

2、迭代器失效的典型场景

场景一:插入操作导致的失效

问题分析:

场景二:删除操作导致的失效

问题分析:(详细的过程可以画图走一下该程序,就能发现存在下面的问题)

3、迭代器失效的解决方案

通用原则

解决方案一:插入操作后的处理

解决方案二:删除操作时的正确写法

关键点:

4、其他可能导致迭代器失效的操作

5、最佳实践建议

6、不同平台对迭代器失效的处理差异

记住:


一、Vector(向量)容器

1、基本概念

vector是C++标准模板库(STL)中表示可变大小数组的序列容器 ,它提供了动态数组的功能,同时具备自动内存管理的特性。

2、存储特性

vector采用连续的内存空间存储元素,这种连续存储的特性带来两个重要优势:

  • 支持随机访问,可以通过下标直接访问元素(时间复杂度O(1))

  • 缓存友好性,由于内存连续性,能获得更好的缓存局部性

3、动态扩容机制

与静态数组不同,vector具有动态改变大小的能力:

  • 当现有空间不足时,vector会执行重新分配

  • 重新分配过程包括:
    a) 分配新的更大的内存空间
    b) 将原有元素移动(或拷贝)到新空间
    c) 释放原有内存空间

  • 典型的扩容策略是按当前大小的某个比例(如2倍)增长

4、空间分配策略

vector采用智能的空间预分配策略:

  • 实际分配的容量(capacity)通常大于当前元素数量(size)

  • 这种超额分配使得在尾部插入操作(push_back)能在平摊常数时间(O(1))内完成

  • 不同实现可能有不同的增长因子,在空间利用和重分配频率间取得平衡

5、操作效率分析

vector在各种操作上的效率表现:

  • 访问元素:高效,O(1)时间复杂度

  • 尾部操作:插入/删除:高效,平摊O(1)

  • 非尾部操作:插入/删除:较低效,O(n)(需要移动后续元素)

  • 与其他序列容器(如list, deque)相比,vector在随机访问和尾部操作上表现最优

6、适用场景

vector特别适合以下场景:

  • 需要频繁随机访问元素

  • 主要在序列尾部进行插入/删除操作

  • 需要内存紧凑布局的场合

  • 元素数量变化较大但大致范围可预估

补充说明:

现代C++标准建议使用vector作为默认的首选容器,除非有特殊需求需要使用其他容器类型。其卓越的性能表现和简单的接口使其成为STL中最常用的容器之一。


二、Vector的定义方式

在C++中,vector是一个动态数组容器,提供了多种灵活的初始化方式。以下是vector的主要定义方式:

1、构造空容器

cpp 复制代码
vector<int> v1;  // 构造一个int类型的空vector
  • 创建一个不包含任何元素的vector

  • 初始容量为0,当添加元素时会自动扩容

2、构造包含n个相同元素的容器

cpp 复制代码
vector<int> v2(10, 2);  // 构造包含10个值为2的int类型vector
  • 第一个参数指定元素数量

  • 第二个参数指定每个元素的值

  • 如果省略第二个参数,将使用值初始化(对于int是0)

3、拷贝构造

cpp 复制代码
vector<int> v3(v2);  // 通过拷贝v2构造v3
  • 创建一个与另一个vector完全相同的新vector

  • 新vector独立于原vector,修改不会相互影响

4、使用迭代器范围构造

cpp 复制代码
vector<int> v4(v2.begin(), v2.end());  // 使用v2的迭代器范围构造v4
  • 通过一对迭代器指定范围来构造vector

  • 范围是左闭右开区间[begin, end)

  • 特别强大,可以用于从其他容器复制数据:

cpp 复制代码
string s("hello world");
vector<char> v5(s.begin(), s.end());  // 从string构造vector<char>

5. 使用初始化列表构造(C++11及以上)

cpp 复制代码
vector<int> v6{1, 2, 3, 4, 5};  // 使用初始化列表构造vector
  • 直接列出所有初始元素

  • 更简洁直观的初始化方式

6. 移动构造(C++11及以上)

cpp 复制代码
vector<int> v7(std::move(v6));  // 使用移动语义构造v7
  • 将资源从v6转移到v7

  • 操作后v6为空,但有效状态

选择哪种初始化方式取决于具体场景和需求,每种方式都有其适用的场合。


三、Vector容器的空间管理与大小

1、size与capacity的区别

在C++的vector容器中,size()capacity()是两个重要但不同的概念:

  • size():返回当前容器中实际存储的元素数量

  • capacity():返回容器在不重新分配内存的情况下可以容纳的最大元素数量

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 2);  // 创建包含10个值为2的元素的vector
    cout << "Size: " << v.size() << endl;      // 输出: 10
    cout << "Capacity: " << v.capacity() << endl; // 输出: 10
    return 0;
}

2、reserve与resize函数

reserve函数

reserve()用于预分配内存空间,改变容器的capacity但不影响size:

  1. 当参数大于当前capacity时,将capacity扩大到该值

  2. 当参数小于等于当前capacity时,不做任何操作

cpp 复制代码
vector<int> v(10, 2);
cout << "Initial size: " << v.size() << endl;      // 10
cout << "Initial capacity: " << v.capacity() << endl; // 10

v.reserve(20);  // 预分配空间为20
cout << "After reserve(20):" << endl;
cout << "Size: " << v.size() << endl;      // 仍为10
cout << "Capacity: " << v.capacity() << endl; // 变为20

resize函数

resize()用于改变容器中元素的数量(size):

  1. 当参数大于当前size时:将size扩大到该值,新增元素初始化为第二个参数值(若不显示给,则默认为0)

  2. 当参数小于当前size时:将size缩小到该值,多余元素被销毁

cpp 复制代码
v.resize(15);  // 将元素数量增加到15
cout << "After resize(15):" << endl;
cout << "Size: " << v.size() << endl;      // 变为15
cout << "Capacity: " << v.capacity() << endl; // 仍为20

v.resize(5);   // 将元素数量减少到5
cout << "After resize(5):" << endl;
cout << "Size: " << v.size() << endl;      // 变为5
cout << "Capacity: " << v.capacity() << endl; // 仍为20

3、empty函数

empty()用于检查容器是否为空(即size是否为0):

cpp 复制代码
vector<int> v(10, 2);
cout << "Is empty? " << boolalpha << v.empty() << endl; // 输出: false

vector<int> emptyV;
cout << "Is empty? " << emptyV.empty() << endl; // 输出: true

解释 boolalpha``(重要!!!)

boolalpha

boolalpha 是 C++ 的一个 I/O 操纵符(manipulator) ,用于控制 bool 值的输出格式。

默认情况下,bool 值在输出时会以 0false)或 1true)的形式显示:

cpp 复制代码
cout << false; // 输出: 0
cout << true;  // 输出: 1

但使用 boolalpha 后,bool 值会以字符串 "false""true" 的形式输出,持续性的:

cpp 复制代码
cout << boolalpha << false; // 输出: false
cout << boolalpha << true;  // 输出: true

在代码中:

cpp 复制代码
cout << "Is empty? " << boolalpha << v.empty() << endl;
  • v.empty() 返回 false(因为 v 有 10 个元素)。

  • boolalpha 确保 false"false" 形式输出,而不是 0

boolalpha 是一个 持久性(sticky) 的 I/O 操纵符(manipulator),这意味着一旦设置后,它会 持续影响后续的 bool 输出 ,直到被显式关闭。可以使用 noboolalpha 来恢复默认的 0/1 输出格式。

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    cout << boolalpha << true << endl;  // 输出: true
    cout << noboolalpha << false << endl; // 输出: 0(恢复默认)
    cout << true << endl;               // 输出: 1(仍然保持默认)
    return 0;
}

4、实际应用建议

  1. 当知道大致元素数量时,使用reserve()预分配空间可以避免多次重新分配

  2. resize()会改变容器内容,而reserve()只影响内存分配

  3. 频繁插入操作时,适当预留空间可以提高性能

  4. shrink_to_fit()(C++11)可以请求减少capacity以匹配size

5、增长机制(了解即可)

在VS和g++环境下运行测试vector的capacity增长机制会发现:VS遵循1.5倍增长策略,而g++采用2倍增长。需要注意的是,vector的扩容倍数并非固定为2倍,具体增长比例由不同版本的STL实现决定。其中VS使用的是PJ版STL,g++采用SGI版STL实现。

关于reserve和resize的区别:

  1. reserve仅预先分配内存空间,不进行初始化,适合预先知道所需空间大小的场景,能有效减少vector扩容带来的性能开销;
  2. resize在分配空间的同时会进行初始化操作,这会直接影响vector的size属性。
cpp 复制代码
// 测试vector的默认扩容机制
void TestVectorExpand() {
    size_t sz;
    vector<int> v;
    sz = v.capacity();
    cout << "Vector capacity growth pattern:\n";
    for (int i = 0; i < 100; ++i) {
        v.push_back(i);
        if (sz != v.capacity()) {
            sz = v.capacity();
            cout << "Capacity changed to: " << sz << endl;
        }
    }
}

VS运行结果(1.5倍扩容):

g++运行结果(2倍扩容):

若已知vector需要存储的元素数量,建议提前预留足够空间。这样可避免插入时频繁扩容导致的性能损耗:

cpp 复制代码
// 使用reserve预分配空间优化性能
void TestVectorExpandOP() {
    vector<int> v;
    size_t sz = v.capacity();
    v.reserve(100);  // 预分配空间避免频繁扩容
    cout << "Vector growth with reserve:\n";
    for (int i = 0; i < 100; ++i) {
        v.push_back(i);
        if (sz != v.capacity()) {
            sz = v.capacity();
            cout << "Capacity changed to: " << sz << endl;
        }
    }
}

四、Vector迭代器使用

1、迭代器概述

迭代器是STL中用于遍历容器元素的工具,它提供了类似指针的接口来访问容器中的元素。vector提供了多种迭代器类型,可以满足不同的遍历需求。

2、正向迭代器

begin()和end()

  • begin(): 返回指向容器第一个元素的正向迭代器

  • end(): 返回指向容器最后一个元素之后位置的迭代器(不是最后一个元素)

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 2); // 创建包含10个2的vector
    
    // 使用正向迭代器遍历容器
    vector<int>::iterator it = v.begin();
    while (it != v.end())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;
    
    // 更简洁的for循环写法(C++11起)
    for(auto num : v) {
        cout << num << " ";
    }
    cout << endl;
    
    return 0;
}

3、反向迭代器

rbegin()和rend()

  • rbegin(): 返回指向容器最后一个元素的反向迭代器

  • rend(): 返回指向容器第一个元素之前位置的反向迭代器

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 2); // 创建包含10个2的vector
    
    // 使用反向迭代器遍历容器
    vector<int>::reverse_iterator rit = v.rbegin();
    while (rit != v.rend())
    {
        cout << *rit << " ";
        rit++;
    }
    cout << endl;
    
    // 使用auto简化迭代器声明(C++11起)
    for(auto rit = v.rbegin(); rit != v.rend(); ++rit) {
        cout << *rit << " ";
    }
    cout << endl;
    
    return 0;
}

4、常量迭代器

vector还提供了常量迭代器版本,用于不允许修改元素的情况:

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5};

// 常量正向迭代器
vector<int>::const_iterator cit = v.cbegin();
while(cit != v.cend()) {
    cout << *cit << " ";
    // *cit = 10; // 错误:不能修改常量迭代器指向的值
    cit++;
}

// 常量反向迭代器
vector<int>::const_reverse_iterator crit = v.crbegin();
while(crit != v.crend()) {
    cout << *crit << " ";
    crit++;
}

5、迭代器使用注意事项

  1. 有效性当vector发生扩容操作后,所有迭代器都会失效

  2. 边界检查:确保迭代器在有效范围内使用

  3. 性能:迭代器访问与下标访问性能相当

  4. 兼容性:迭代器使算法可以独立于具体容器实现

6、实际应用示例

cpp 复制代码
#include <algorithm>

vector<int> v = {5, 3, 1, 4, 2};

// 使用迭代器配合STL算法
sort(v.begin(), v.end()); // 排序

// 查找元素
auto found = find(v.begin(), v.end(), 3);
if(found != v.end()) {
    cout << "Found at position: " << distance(v.begin(), found) << endl;
}

// 使用迭代器区间构造新vector
vector<int> v2(v.begin()+1, v.end()-1);

通过合理使用各种迭代器,可以灵活高效地操作vector容器中的元素。


五、Vector的增删查改操作

1、尾部操作:push_back和pop_back

push_backpop_back是vector最基本的操作函数,用于在容器尾部进行元素的添加和删除。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;

    // 使用push_back在尾部插入元素
    v.push_back(1); // 尾插元素1
    v.push_back(2); // 尾插元素2
    v.push_back(3); // 尾插元素3
    v.push_back(4); // 尾插元素4

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    // 使用pop_back删除尾部元素
    v.pop_back(); // 尾删元素4
    v.pop_back(); // 尾删元素3
    v.pop_back(); // 尾删元素2
    v.pop_back(); // 尾删元素1

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

注意事项

  • push_back的时间复杂度为O(1)(平均情况),当容量不足时会触发重新分配内存

  • 对空vector使用pop_back会导致未定义行为

2、任意位置操作:insert和erase

通过insert函数可以在所给迭代器位置插入一个或多个元素,通过erase函数可以删除所给迭代器位置的元素,或删除所给迭代器区间内的所有元素(左闭右开)。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    // 在开头插入元素0
    v.insert(v.begin(), 0);

    // 在开头插入5个-1
    v.insert(v.begin(), 5, -1);

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    // 删除第一个元素
    v.erase(v.begin());

    // 删除前5个元素(左闭右开区间)
    v.erase(v.begin(), v.begin() + 5);

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

时间复杂度分析

  • inserterase操作的时间复杂度为O(n),因为可能需要移动后续元素

  • 在vector头部操作效率最低,尾部操作效率最高

以上是按位置进行插入或删除元素的方式,若要按值进行插入或删除(在某一特定值位置进行插入或删除),则需要用到find函数。

3、查找操作:find函数

  • 标准库的find函数可以用于在vector中查找特定值。find函数是在算法模块(algorithm)当中实现的,不是vector的成员函数。
  • find函数接受三个参数,前两个参数指定一个左闭右开的迭代器范围,第三个参数是要查找的值。该函数会在给定范围内查找第一个匹配的元素,并返回其迭代器;若未找到,则返回第二个参数。
cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>  // 包含find函数
using namespace std;

int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    // 查找值为2的元素
    auto pos = find(v.begin(), v.end(), 2);

    if (pos != v.end()) {
        // 在2的位置插入10
        v.insert(pos, 10);
    }

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    // 查找值为3的元素
    pos = find(v.begin(), v.end(), 3);

    if (pos != v.end()) {
        // 删除3
        v.erase(pos);
    }

    for (auto num : v)
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

注意事项

  • find是线性查找,时间复杂度为O(n)

  • 对于有序vector,可以使用binary_search等更高效的算法

  • 务必检查返回值是否为v.end(),否则可能导致未定义行为

4、交换操作:swap

swap函数可以高效交换两个vector的内容。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v1(10, 1); // 10个1
    vector<int> v2(10, 2); // 10个2

    // 交换v1和v2的内容
    v1.swap(v2);

    for (auto num : v1)
    {
        cout << num << " ";
    }
    cout << endl;

    for (auto num : v2)
    {
        cout << num << " ";
    }
    cout << endl;

    // 也可以使用标准库的swap
    swap(v1, v2);

    for (auto num : v1)
    {
        cout << num << " ";
    }
    cout << endl;

    for (auto num : v2)
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

特点

  • 交换操作非常高效,时间复杂度O(1)

  • 仅交换内部指针,不涉及元素的实际移动

5、元素访问方式

vector提供了多种元素访问方式,各有适用场景。

下标访问

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 1);
    
    // 使用下标访问
    for (size_t i = 0; i < v.size(); i++) {
        cout << v[i] << " ";  // 使用operator[]
        // 也可以使用v.at(i),会进行边界检查
    }
    cout << endl;
    
    return 0;
}

迭代器访问

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 1);
    
    // 使用迭代器
    for (auto it = v.begin(); it != v.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    return 0;
}

我们上面说到vector是支持迭代器的,所以我们还可以用范围for对vector容器进行遍历。(支持迭代器就支持范围for,因为在编译时编译器会自动将范围for替换为迭代器的形式)

范围for循环

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(10, 1);
    
    // 范围for循环(底层使用迭代器实现)
    for (auto e : v) {
        cout << e << " ";
    }
    cout << endl;
    
    return 0;
}

访问方式比较

  1. 下标访问:最直观,性能好,但不进行边界检查

  2. at()方法:进行边界检查,越界时抛出异常

  3. 迭代器:更通用的访问方式,支持所有标准容器

  4. 范围for:C++11引入,代码最简洁

总结

vector作为C++中最常用的序列容器,提供了丰富的操作接口:

  • push_back, insert

  • pop_back, erase, clear

  • find, operator[], at

  • :通过迭代器或下标直接修改

  • 其他swap, size, empty, reserve

合理选择操作方式可以显著提高程序效率,特别是在处理大量数据时。


六、Vector迭代器失效问题

1、迭代器失效的概念

迭代器是STL中用于遍历容器元素的工具,它提供了一种统一的方式来访问各种容器,而不需要关心底层的数据结构实现。 对于vector而言,其迭代器在底层实际上就是一个原生指针(T*)

迭代器失效指的是迭代器底层对应的指针所指向的空间已经被销毁,导致迭代器指向了一块无效的内存区域。如果继续使用已经失效的迭代器,可能会导致程序崩溃或产生未定义行为。

2、迭代器失效的典型场景

场景一:插入操作导致的失效

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{1, 2, 3, 4, 5}; // 初始化vector: 1 2 3 4 5
    
    // 获取值为2的元素的迭代器
    vector<int>::iterator pos = find(v.begin(), v.end(), 2); 
    
    v.insert(pos, 10); // 在2的位置插入10 → 1 10 2 3 4 5
    
    // 危险!pos已经失效,可能指向错误位置或已释放的内存
    v.erase(pos); 
    
    return 0;
}

在该代码中,我们本意是使用元素2的迭代器在原序列中2的位置插入一个10,然后将2删除,但我们实际上获取的是指向2的指针,当我们在2的位置插入10后,该指针就指向了10,所以我们之后删除的实际上是10,而不是2。

问题分析
  1. 当在vector中间插入元素时,可能导致内存重新分配

  2. 即使没有重新分配内存,插入点后的所有元素都会向后移动

  3. 原迭代器pos仍然指向原来的内存地址,但该位置的内容可能已经改变

  4. 继续使用失效的迭代器会导致未定义行为

场景二:删除操作导致的失效

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{1, 2, 3, 4, 5, 6};
    
    vector<int>::iterator it = v.begin();
    while (it != v.end()) {
        if (*it % 2 == 0) { // 删除所有偶数
            v.erase(it);    // 删除后it失效
        }
        it++;              // 危险!对失效迭代器进行递增
    }
    return 0;
}
问题分析:(详细的过程可以画图走一下该程序,就能发现存在下面的问题)
  1. erase操作会删除指定位置的元素,并使该位置之后的所有元素前移

  2. 删除操作会使指向被删除元素及其后所有元素的迭代器失效

  3. 直接对失效的迭代器进行递增操作会导致未定义行为

  4. 此外,这种写法会跳过对某些元素的检查(因为删除后it++会跳过一个元素)

3、迭代器失效的解决方案

通用原则

每次修改容器后,都应该重新获取迭代器,不要继续使用旧的迭代器。

解决方案一:插入操作后的处理

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{1, 2, 3, 4, 5};
    
    // 获取值为2的元素的迭代器
    vector<int>::iterator pos = find(v.begin(), v.end(), 2);
    
    v.insert(pos, 10); // 插入后pos失效
    
    // 重新获取值为2的元素的迭代器
    pos = find(v.begin(), v.end(), 2);
    
    if (pos != v.end()) {
        v.erase(pos); // 安全删除
    }
    
    // 输出结果: 1 10 3 4 5
    for (auto num : v) {
        cout << num << " ";
    }
    return 0;
}

解决方案二:删除操作时的正确写法

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{1, 2, 3, 4, 5, 6};
    
    vector<int>::iterator it = v.begin();
    while (it != v.end()) {
        if (*it % 2 == 0) {
            // erase返回指向被删除元素下一个位置的迭代器
            it = v.erase(it); 
        } else {
            it++; // 只有未删除时才递增
        }
    }
    
    // 输出结果: 1 3 5
    for (auto num : v) {
        cout << num << " ";
    }
    return 0;
}

对于实例二,我们可以接收erase函数的返回值(erase函数返回删除元素的后一个元素的新位置),并且控制代码的逻辑:当元素被删除后继续判断该位置的元素(因为该位置的元素已经更新,需要再次判断)。

关键点
  1. 使用erase的返回值更新迭代器

  2. 只有不删除元素时才手动递增迭代器

  3. 确保每次循环都正确处理迭代器状态

4、其他可能导致迭代器失效的操作

  1. push_back/emplace_back:当导致vector容量改变时,所有迭代器都会失效

  2. resize/reserve:可能改变vector的存储位置

  3. assign:替换所有元素,使所有迭代器失效

  4. clear:清空容器,使所有迭代器失效

5、最佳实践建议

  1. 在修改容器后,总是假定所有迭代器都可能失效

  2. 尽量在修改操作后重新获取迭代器

  3. 对于删除操作,使用erase的返回值更新迭代器

  4. 考虑使用索引而非迭代器进行遍历和修改(在某些简单场景下)

  5. 在C++11及以后版本,可以考虑使用基于范围的for循环,但注意不能在循环内修改容器结构

6、不同平台对迭代器失效的处理差异

在Linux环境下,g++编译器对迭代器失效的检测相对宽松,处理方式没有Visual Studio那么严格。这意味着在某些迭代器失效的情况下,程序可能不会立即崩溃,但会导致未定义行为和错误的结果。

记住:

预防迭代器失效的关键在于理解容器操作对内存布局的影响,并在每次修改操作后谨慎处理迭代器。

相关推荐
十五年专注C++开发40 分钟前
CMake进阶: externalproject_add用于在构建阶段下载、配置、构建和安装外部项目
linux·c++·windows·cmake·自动化构建
kyle~1 小时前
Qt---Qt函数库
开发语言·qt
Acrelhuang2 小时前
基于柔性管控终端的新能源汽车充电站有序充电系统设计与实现
java·开发语言·人工智能
麻雀无能为力2 小时前
python自学笔记8 二维和三维可视化
开发语言·笔记·python
码与农2 小时前
C++设计模式
开发语言·c++
Tipriest_2 小时前
CMake message()使用指南
c++·cmake·message
ruangongtaotao3 小时前
java python
java·开发语言·python
企鹅chi月饼3 小时前
c++中的Lambda表达式详解
c++·lambda
minji...3 小时前
算法题Day1
c++·算法