C++:vector容器

前言:std::vector 是 C++ 标准模板库(STL)中最常用的容器之一,本质是动态数组,支持动态扩容,兼具数组的高效访问和链表的灵活扩容特性。

一、vector 核心特性

  1. 动态大小:初始化时可指定初始大小,后续可自动扩容(无需手动管理内存)。
  2. 连续内存:元素在内存中连续存储(同数组),支持随机访问(通过下标快速访问元素)。
  3. 模板实现 :支持存储任意数据类型(如 vector<int>vector<string>vector<自定义类>)。
  4. 高效性:尾部插入 / 删除元素效率高(时间复杂度 O (1)),中间插入 / 删除效率低(需移动元素,O (n))。

二、基本使用步骤

1. 头文件与命名空间

cpp 复制代码
#include <vector>  // 必须包含的头文件
using namespace std;  // 简化写法,否则需用 std::vector

2. 初始化方式

cpp 复制代码
// 方式1:默认初始化(空vector)
vector<int> v1;

// 方式2:指定大小和初始值(10个元素,每个值为5)
vector<int> v2(10, 5);

// 方式3:用初始化列表(C++11+)
vector<int> v3 = {1, 2, 3, 4, 5};

// 方式4:复制另一个vector
vector<int> v4(v3);  // v4 是 v3 的副本

三、常用成员函数(操作)

1. 元素访问

函数 功能 示例
operator[] 通过下标访问(无越界检查) v[2] = 10;
at(index) 通过下标访问(有越界检查,抛异常) v.at(2) = 10;
front() 获取第一个元素 int first = v.front();
back() 获取最后一个元素 int last = v.back();
data() 返回指向底层数组的指针(直接操作内存) int* ptr = v.data();

2. 增删元素

函数 功能 示例
push_back(val) 在尾部添加元素 v.push_back(6);
pop_back() 删除尾部元素 v.pop_back();
insert(pos, val) 在指定位置插入元素(pos 是迭代器) v.insert(v.begin()+2, 99);
erase(pos) 删除指定位置元素 v.erase(v.begin()+1);
clear() 清空所有元素(大小变为 0,容量不变) v.clear();

3. 容量与大小

函数 功能 示例
size() 返回当前元素个数 int len = v.size();
capacity() 返回当前容量(可容纳的最大元素数) int cap = v.capacity();
empty() 判断是否为空(size 为 0) if (v.empty()) { ... }
reserve(n) 预留容量(避免多次扩容) v.reserve(100);
resize(n, val) 调整大小(多删少补,补 val 默认 0) v.resize(8, 0);

四、迭代器(遍历元素)

迭代器是访问容器元素的通用方式,类似指针,支持遍历、修改元素:

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

// 正向遍历
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";  // *it 访问元素
}
// 输出:1 2 3 4 5

// 反向遍历
for (vector<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit) {
    cout << *rit << " ";
}
// 输出:5 4 3 2 1

// C++11 简化写法(范围for循环)
for (int num : v) {
    cout << num << " ";
}

五、底层原理(为什么高效?)

  1. 内存布局

    元素存储在连续的动态数组 中,底层通过指针管理内存(类似 new int[n])。

  2. 扩容机制

    size() == capacity() 时,插入元素会触发扩容:

    • 申请一块更大的内存(通常是原容量的 1.5~2 倍,不同编译器实现不同)。
    • 将原元素复制到新内存。
    • 释放原内存。
      注意:扩容后原迭代器、指针、引用会失效(指向旧内存)。
  3. reserve () 的作用

    提前预留容量(如 v.reserve(100)),可避免多次扩容的性能损耗(复制元素耗时)。

六、实战示例:综合操作

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

int main() {
    // 初始化一个vector
    vector<string> fruits;
    
    // 添加元素
    fruits.push_back("apple");
    fruits.push_back("banana");
    fruits.insert(fruits.begin(), "orange");  // 在开头插入
    
    // 遍历输出
    cout << "当前水果:";
    for (const string& f : fruits) {  // const 避免修改,& 避免复制
        cout << f << " ";
    }
    // 输出:当前水果:orange apple banana
    
    // 容量与大小
    cout << "\n大小:" << fruits.size()  // 3
         << ",容量:" << fruits.capacity() << endl;  // 可能为4(取决于编译器)
    
    // 修改元素
    fruits[1] = "grape";
    cout << "修改后:" << fruits[1] << endl;  // grape
    
    // 删除元素
    fruits.erase(fruits.end() - 1);  // 删除最后一个元素(banana)
    
    // 清空
    fruits.clear();
    cout << "清空后大小:" << fruits.size() << endl;  // 0
    
    return 0;
}

七、注意事项

  1. 避免频繁扩容

    已知大致元素数量时,先用 reserve(n) 预留容量,减少性能损耗。

  2. 迭代器失效问题

    • push_back() 可能导致扩容,使所有迭代器失效。
    • insert()/erase() 会使插入 / 删除位置后的迭代器失效。
  3. 与数组的区别

    • 数组是静态大小(编译期确定),vector 是动态大小(运行期调整)。
    • 数组无需额外内存 overhead,vector 需存储容量等信息(微小开销)。
  4. 存储自定义类型

    需确保自定义类有默认构造函数(resize() 等操作会用到)。

八、总结

vector 是 C++ 中最常用的容器,平衡了效率和灵活性,适合需要频繁访问元素、尾部增删的场景(如存储列表数据、临时缓存等)。掌握其底层扩容机制和迭代器使用,能写出更高效、安全的代码。