【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。

cpp 复制代码
// 测试vector的默认扩容机制 
void TestVectorExpand() 
{ 
    size_t sz;  
    vector<int> 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'; 
         } 
     } 
}
cpp 复制代码
// 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够 
// 就可以避免边插入边扩容导致效率低下的问题了 
void TestVectorExpandOP() 
{  
    vector<int> 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 插入操作详解

cpp 复制代码
vector<int> 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<int> 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 删除操作详解

cpp 复制代码
vector<int> 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<int> 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 查找操作扩展

cpp 复制代码
#include <algorithm>
#include <vector>

vector<int> 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 << endl;
}

// 查找第一个满足条件的元素
auto it2 = find_if(v.begin(), v.end(), [](int x) { return x > 3; });

// 查找子序列
vector<int> sub = {3, 4};
auto it3 = search(v.begin(), v.end(), sub.begin(), sub.end());

Lambda 表达式

cpp 复制代码
[capture](parameters) -> return_type { function_body }

具体分解

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

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

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

Lambda 表达式的特性

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

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

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

等价函数

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

cpp 复制代码
bool greater_than_three(int x) {
    return x > 3;
}

Lambda 表达式的更多用法

捕获外部变量

cpp 复制代码
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; });

指定返回类型

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

复杂逻辑

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

4.5 访问操作扩展

cpp 复制代码
vector<int> 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 用指针等),违背了代码复用的原则。

相关推荐
利刃大大5 小时前
【高并发内存池】三、线程缓存的设计
开发语言·c++·缓存·项目·内存池
Miracle&5 小时前
C++后端开发重点知识点
开发语言·c++
1白天的黑夜15 小时前
哈希表-面试题01.02.判定是否互为字符重排-力扣(LeetCode)
c++·leetcode·哈希表
怀旧,5 小时前
【C++】15. ⼆叉搜索树
开发语言·c++
先做个垃圾出来………5 小时前
242. 有效的字母异位词| 349. 两个数组的交集
开发语言·python
Tipriest_5 小时前
C++关于手动抛出异常,自动抛出异常以及一些不会抛出异常的情况说明
开发语言·c++·异常·exception
程序员老舅5 小时前
‌NAT穿透技术原理:P2P通信中的打洞机制解析‌
服务器·c++·网络协议·网络编程·p2p·nat·网络穿透
凤年徐5 小时前
【数据结构】八大排序之快速排序:分而治之的艺术
c语言·开发语言·数据结构·c++·算法·排序算法
啊吧怪不啊吧6 小时前
C++之基于正倒排索引的Boost搜索引擎项目介绍
c++·http·搜索引擎