> 技术文档 > 【C++】 Vector容器操作全解析

【C++】 Vector容器操作全解析


C++ Vector 容器详解

1. 容量与大小管理

1.1 基本容量操作

  • size():返回当前元素个数

  • capacity():返回当前分配的内存容量(可容纳的元素数)

  • empty():判断容器是否为空

1.2 容量调整操作

  • resize(n):改变 size

    • 若 n > size,新增元素默认初始化(对于基本类型为0,类类型调用默认构造函数

    • 若 n < size,删除尾部多余元素

    • 可选第二参数指定初始化值:resize(n, value)

  • reserve(n):改变 capacity

    • 只开空间,不初始化,不影响 size

    • 如果 n < capacity,通常无效果(实现可能忽略)

    • 如果 n > capacity,重新分配内存,可能导致迭代器失效

2. Vector 的扩容机制

2.1 不同编译器的增长策略

  • VS (PJ STL):1.5 倍增长

  • g++ (SGI STL):2 倍增长

  • Clang (通常):2 倍增长

2.2 扩容性能分析

  • 摊销常数时间:虽然单次扩容可能耗时,但多次操作的均摊时间复杂度为 O(1)

  • 扩容代价:需要分配新内存、拷贝元素、释放旧内存

  • reserve 只负责开辟空间,如果确定知道需要用多少空间,reserve 可以缓解 vector 增容的代价缺陷问题。

  • resize 在开空间的同时还会进行初始化,影响 size。

// 测试vector的默认扩容机制 void TestVectorExpand() { size_t sz; vector v; sz = v.capacity(); cout << \"making v grow:\\n\"; for (int i = 0; i < 100; ++i) { v.push_back(i); if (sz != v.capacity())  {  sz = v.capacity();  cout << \"capacity changed: \" << sz << \'\\n\'; } } }

// 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够 // 就可以避免边插入边扩容导致效率低下的问题了 void TestVectorExpandOP() { vector v; size_t sz = v.capacity(); v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容 cout << \"making bar grow:\\n\"; for (int i = 0; i < 100; ++i) { v.push_back(i); if (sz != v.capacity()) {  sz = v.capacity();  cout << \"capacity changed: \" << sz << \'\\n\'; } } }

1.2.4vector 增删查改

push_back(val):尾部插入(拷贝或移动)

pop_back():尾部删除(不返回被删除元素)

emplace_back(args...)(C++11):直接在尾部构造元素,避免拷贝

emplace(pos, args...)(C++11):在指定位置直接构造元素

find 查找(注意这个是算法模块实现,不是 vector 的成员接口)

insert 在 position 之前插入 val

erase 删除 position 位置的数据

swap 交换两个 vector 的数据空间

operator[] (重点) 像数组一样访问

4.2 插入操作详解

vector v = {1, 3, 4};// 插入单个元素v.insert(v.begin() + 1, 2); // v = {1, 2, 3, 4}// 插入多个相同元素v.insert(v.end(), 3, 5); // v = {1, 2, 3, 4, 5, 5, 5}// 插入范围vector other = {6, 7, 8};v.insert(v.end(), other.begin(), other.end()); // v = {1, 2, 3, 4, 5, 5, 5, 6, 7, 8}// 使用初始化列表插入(C++11)v.insert(v.begin(), {0, -1}); // v = {-1, 0, 1, 2, 3, 4, 5, 5, 5, 6, 7, 8}

4.3 删除操作详解

vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9};// 删除单个元素v.erase(v.begin()); // 删除第一个元素// 删除范围v.erase(v.begin() + 2, v.begin() + 4); // 删除第3和第4个元素// 删除所有元素v.clear();// 删除满足条件的元素(使用erase-remove惯用法)vector v2 = {1, 2, 3, 4, 5, 6, 7, 8, 9};v2.erase(remove_if(v2.begin(), v2.end(), [](int x) { return x % 2 == 0; }), v2.end());// 删除所有偶数

4.4 查找操作扩展

#include #include vector v = {1, 2, 3, 4, 5, 3, 2, 1};// 查找第一个匹配元素auto it = find(v.begin(), v.end(), 3);if (it != v.end()) { cout << \"Found at position: \" << distance(v.begin(), it) << endl;}// 查找最后一个匹配元素auto rit = find(v.rbegin(), v.rend(), 3);if (rit != v.rend()) { cout << \"Last found at position: \" << distance(v.begin(), rit.base()) - 1 < 3; });// 查找子序列vector sub = {3, 4};auto it3 = search(v.begin(), v.end(), sub.begin(), sub.end());

Lambda 表达式

[capture](parameters) -> return_type { function_body }

具体分解

  • []: 捕获列表(空,表示不捕获任何外部变量)

  • (int x): 参数列表(接受一个整数参数 x)

  • { return x > 3; }: 函数体(返回 x > 3 的比较结果)

Lambda 表达式的特性

  1. 匿名函数:没有函数名,直接定义和使用

  2. 类型推导:编译器自动推导返回类型(这里返回 bool

  3. 捕获能力:可以通过捕获列表访问外部变量(这里没有使用)

等价函数

这个 lambda 表达式等价于以下传统函数:

bool greater_than_three(int x) { return x > 3;}

Lambda 表达式的更多用法

捕获外部变量

int threshold = 3;// 通过值捕获外部变量auto it = find_if(v.begin(), v.end(), [threshold](int x) { return x > threshold; });// 通过引用捕获外部变量auto it = find_if(v.begin(), v.end(), [&threshold](int x) { return x > threshold; });

指定返回类型

// 显式指定返回类型auto it = find_if(v.begin(), v.end(), [](int x) -> bool { return x > 3; });

复杂逻辑

// 多个条件的lambdaauto it = find_if(v.begin(), v.end(), [](int x) { return x > 3 && x % 2 == 0; // 大于3的偶数});

4.5 访问操作扩展

vector v = {1, 2, 3, 4, 5};// 使用operator[](不检查边界)int a = v[2]; // a = 3// 使用at()(检查边界)try { int b = v.at(10); // 抛出std::out_of_range异常} catch (const out_of_range& e) { cout << \"Out of range: \" << e.what() << endl;}// 访问首尾元素int first = v.front(); // 等同于v[0]int last = v.back(); // 等同于v[v.size()-1]// 直接访问底层数据int* data = v.data(); // 获取指向底层数组的指针

标准库算法 std::find 的返回值是迭代器:

C++ 标准库的算法(如 findsort 等)是通用的,可以适配各种容器(vectorlistset 等)。

  • 迭代器为不同容器提供了统一的访问接口:无论容器内部存储结构如何(数组、链表等),算法都能通过迭代器遍历和操作元素。

  • 若不使用迭代器,find 函数需要为每种容器单独实现(如 vector 用索引,list 用指针等),违背了代码复用的原则。