前言:std::vector
是 C++ 标准模板库(STL)中最常用的容器之一,本质是动态数组,支持动态扩容,兼具数组的高效访问和链表的灵活扩容特性。
一、vector 核心特性
- 动态大小:初始化时可指定初始大小,后续可自动扩容(无需手动管理内存)。
- 连续内存:元素在内存中连续存储(同数组),支持随机访问(通过下标快速访问元素)。
- 模板实现 :支持存储任意数据类型(如
vector<int>
、vector<string>
、vector<自定义类>
)。- 高效性:尾部插入 / 删除元素效率高(时间复杂度 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);
四、迭代器(遍历元素)
迭代器是访问容器元素的通用方式,类似指针,支持遍历、修改元素:
cppvector<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 << " "; }
五、底层原理(为什么高效?)
内存布局 :
元素存储在连续的动态数组 中,底层通过指针管理内存(类似
new int[n]
)。扩容机制 :
当
size() == capacity()
时,插入元素会触发扩容:
- 申请一块更大的内存(通常是原容量的 1.5~2 倍,不同编译器实现不同)。
- 将原元素复制到新内存。
- 释放原内存。
注意:扩容后原迭代器、指针、引用会失效(指向旧内存)。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; }
七、注意事项
避免频繁扩容 :
已知大致元素数量时,先用
reserve(n)
预留容量,减少性能损耗。迭代器失效问题:
push_back()
可能导致扩容,使所有迭代器失效。insert()
/erase()
会使插入 / 删除位置后的迭代器失效。与数组的区别:
- 数组是静态大小(编译期确定),
vector
是动态大小(运行期调整)。- 数组无需额外内存 overhead,
vector
需存储容量等信息(微小开销)。存储自定义类型 :
需确保自定义类有默认构造函数(
resize()
等操作会用到)。
八、总结
vector
是 C++ 中最常用的容器,平衡了效率和灵活性,适合需要频繁访问元素、尾部增删的场景(如存储列表数据、临时缓存等)。掌握其底层扩容机制和迭代器使用,能写出更高效、安全的代码。