【C++STL】vector详解
目录
0.Vector文档介绍
1.Vector 基本概念
vector 是 C++ 标准模板库(STL)中的一个序列容器,提供动态数组的功能。
使用连续存储位置来存储其元素,向量的大小可以动态变化,其存储由容器自动管理。
与其他动态序列容器(双端队列、列表和前向列表)相比,向量在访问元素时效率极高(与数组类似),且在末尾添加或删除元素时也相对高效。然而,对于涉及在非末尾位置插入或删除元素的操作,其性能不如其他容器,并且其迭代器和引用的稳定性也低于列表和前向列表。
主要特性:
动态扩容:自动管理内存,根据需要动态调整大小
随机访问:支持 O(1) 时间复杂度的元素访问
连续存储:元素在内存中连续存储
模板类:可以存储任意类型的数据
一句话总结:
Vector = 动态数组 + 自动内存管理 + 快速随机访问 - 中间操作代价
Vector 的优缺点
优点:
- 随机访问快:O(1)时间复杂度
- 缓存友好: 内存连续,CPU预取效率高
- 尾部操作高效: push_back/pop_back平均O(1)
- 内存紧凑: 无额外指针开销
- 与C数组兼容: 可直接获取底层指针
缺点:
- 中间插入删除慢: O(n)时间复杂度,需要移动元素
- 扩容成本高: 需要重新分配内存和拷贝数据
- 容量可能浪费: 预分配的空间可能用不完
- 迭代器易失效: 插入删除或扩容后迭代器可能失效
- 头部操作效率低: 在开头插入删除需要移动所有元素
3. Vector 的内存模型
template<class T>
class vector {
private:
T* start; // 指向数据块的开始
T* finish; // 指向最后一个有效元素的下一个位置
T* end_Of_storage; // 指向存储容量的尾
};

4.vector常用接口的使用
4.1vector的常见构造(初始化)

无参构造函数 vector()
vector<int> v; // 创建空vector填充构造函数 vector(n, val)
vector<int> v(5, 10); // 5个10 vector<int> v(3); // 3个0(默认值)拷贝构造函数 vector(const vector& x)
vector<int> v1 = {1, 2, 3}; vector<int> v2(v1); // 深拷贝范围构造函数 vector(first, last)
int arr[] = {1, 2, 3}; vector<int> v(arr, arr + 3); // 从数组构造 vector<int> source = {1, 2, 3, 4}; vector<int> v2(source.begin(), source.end() - 1); // 从vector子范围构造
4.2vector的遍历及迭代器
| 遍历方式 | 优点 | 缺点 | 适用场景 |
| operator[] | 简单直观,可随机访问 | 需要知道size() | 需要索引或随机访问 |
| 迭代器 | 灵活,支持算法,通用 | 语法稍复杂 | 需要修改元素或使用算法 |
| 范围for | 简洁,易读 | 不能控制步长 | 简单遍历,C++11以上 |
| at | 边界检查,异常抛出 | 有检查开销 | 用户输入、不确定的索引 |
operator[]的使用
operator[] 是 vector 最高效的访问方式,但需要程序员自己保证索引的有效性。在性能关键的代码和已知索引安全的场景中应该优先使用。
// 性能关键循环
for (size_t i = 0; i < vec.size(); ++i) {
result += vec[i] * weights[i]; // 快速无检查
}
// 已知索引有效
void update_element(vector<int>& vec, size_t index, int value) {
if (index < vec.size()) { // 手动检查
vec[index] = value; // 然后安全使用operator[]
}
}
// 数学计算
vector<double> normalize(const vector<double>& data) {
vector<double> result(data.size());
for (size_t i = 0; i < data.size(); ++i) {
result[i] = data[i] / data[0]; // 快速访问
}
return result;
}
at()的使用
at() 函数提供了安全的元素访问机制,特别适合处理不可信的输入数据或在需要健壮性的场景中使用。
// 处理用户输入
int user_index = get_user_input();
try {
string value = data.at(user_index);
process(value);
} catch (const out_of_range& e) {
show_error("无效的索引");
}
// 处理外部数据
vector<int> process_indices = get_external_indices();
for (int idx : process_indices) {
try {
result += data.at(idx);
} catch (const out_of_range& e) {
log_error("跳过无效索引: " + to_string(idx));
}
}
vector iterator 的使用
| iterator的使用 | 接口说明 |
| begin + end(重点) | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置 的iterator/const_iterator |
| rbegin和rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator |


迭代器分类
通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器。
通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器。
vector<int> vec = {1, 2, 3, 4, 5};
// 正向迭代器
vector<int>::iterator it = vec.begin(); // 可读写
vector<int>::const_iterator cit = vec.cbegin(); // 只读
// 反向迭代器
vector<int>::reverse_iterator rit = vec.rbegin(); // 可读写反向
vector<int>::const_reverse_iterator crit = vec.crbegin(); // 只读反向
迭代器操作
vector<int>::iterator it = vec.begin();
// 基本操作
*it; // 解引用
++it; // 前进到下一个
--it; // 后退到上一个
it + 3; // 前进3个位置
it1 == it2; // 比较是否指向同一位置
it1 < it2; // 比较位置前后
// 特殊位置
vec.begin(); // 指向第一个元素
vec.end(); // 指向最后一个元素的下一个位置
vec.rbegin(); // 指向最后一个元素
vec.rend(); // 指向第一个元素的前一个位置
迭代器失效规则
插入操作
vector<int> vec = {1, 2, 3, 4, 5}; auto it = vec.begin() + 2; // 指向3 // 插入可能导致重新分配 vec.push_back(6); // it 可能失效!不要继续使用 // 安全做法:重新获取迭代器 it = vec.begin() + 2;
删除操作
vector<int> vec = {1, 2, 3, 4, 5}; auto it = vec.begin() + 2; // 指向3 vec.erase(vec.begin() + 1); // 删除2 // it 现在失效!指向的位置可能已改变 // 安全做法:使用erase返回值 it = vec.erase(vec.begin() + 1);
范围for
for (range_declaration : range_expression) {
// 循环体
}
//range_declaration:声明一个变量,用于存储范围中的每个元素
//range_expression:任何可以返回序列的表达式(vector、array、string等)
简单演示:(遍历v中的变量)
int main()
{
vector<int> v(2, 10);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
范围for循环在编译时会被展开为传统的迭代器代码:
// 范围for
for (const auto& element : container) {
// 处理element
}
// 等价于
{
auto&& __range = container;
auto __begin = begin(__range);
auto __end = end(__range);
for (; __begin != __end; ++__begin) {
const auto& element = *__begin;
// 处理element
}
}
4.3vector 空间增长问题
| 容量空间 | 接口说明 |
| size | 获取数据个数 |
| capacity | 获取容量大小 |
| empty | 判断是否为空 |
| resize(重点) | 改变vector的size |
| reserve(重点) | 改变vector的capacity |
- capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义 的。vs是PJ版本STL,g++是SGI版本STL。
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问 题。
- resize在开空间的同时还会进行初始化,影响size。
reserve() - 预分配空间
vector<int> vec; vec.reserve(100); // 预分配100个元素的空间 // 内部实现大致如下: void reserve(size_t n) { if (n > capacity()) { // 1. 分配新内存 // 2. 拷贝现有元素 // 3. 释放旧内存 // 4. 更新指针 } }
resize() - 调整大小
vector<int> vec; vec.resize(10); // size=10, capacity>=10 vec.resize(5); // size=5, capacity不变 vec.resize(20, 1); // size=20, 新元素初始化为1
4.4vector 增删查改
| vector增删查改 | 接口说明 |
| push_back(重点) | 尾插 |
| pop_back(重点) | 尾删 |
| find | 查找。(注意这个是算法模块实现,不是vector的成员接口) |
| insert | 在position之前插入val |
| erase | 删除position位置的数据 |
| swap | 交换两个vector的数据空间 |
push_back:
void push_back(const T& value);
这个接口的功能很明确,就是在尾部插入数据
int main() { vector<int> v; for (int i = 0; i < 5; i++) { v.push_back(i); } for (auto ch : v) { cout << ch << " "; } cout << endl; return 0; } //0 1 2 3 4
pop_back:
void pop_back();
相反,这个接口就是去尾删最后一个元素
int main() { vector<int> v(5, 2); for (auto ch : v) { cout << ch << " "; } cout << endl; for (int i = 0; i < 3; i++) { v.pop_back(); } for (auto ch : v) { cout << ch << " "; } cout << endl; return 0; } //2 2 2 2 2 2 2
find:
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);这个接口是封装在了 <algorithm> 这个头文件中的一种算法。
返回一个迭代器,指向范围[first,last)中与val相等的第一个元素。如果没有找到这样的元素,则函数返回last。
该函数使用运算符==将单个元素与val进行比较。常用于我们要去删除一个指定的数据,传入指定的搜索区间和要查找的值,若是返回的迭代器位置没有到达末尾的话,代表找到了这个值,我们去删除这个迭代器即可
int main() { int a[] = { 1,2,7,4,5,1,2,5,7,6 }; vector<int> v(a, a + 10); vector<int>::iterator pos = find(v.begin(), v.end(), 7); if (pos != v.end()) { v.erase(pos); } for (auto ch : v) { cout << ch << " "; } cout << endl; return 0; } //1 2 4 5 1 2 5 7 6
insert:

对于insert这个接口,这里只介绍较为常见的两个重载
iterator insert (const_iterator position, const value_type& val);
在指定位置插入一个元素,返回指向新插入元素的迭代器
int main() { vector<int> vec = {10, 20, 30, 40}; cout << "插入前: "; for (int n : vec) cout << n << " "; cout << endl; // 在位置2插入25 auto it = vec.insert(vec.begin() + 2, 25); cout << "插入后: "; for (int n : vec) cout << n << " "; cout << endl; cout << "返回值指向: " << *it << endl; cout << "新元素位置: " << (it - vec.begin()) << endl; return 0; } // 插入前: 10 20 30 40 插入后: 10 20 25 30 40 返回值指向: 25 新元素位置: 2
iterator insert (const_iterator position, size_type n, const value_type& val);
在 vector 的指定位置插入 n 个相同的元素,返回指向第一个新插入元素的迭代器
int main() { vector<int> vec = {1, 2, 3, 4}; cout << "插入前: "; for (int n : vec) cout << n << " "; cout << endl; // 在位置1插入3个99 auto it = vec.insert(vec.begin() + 1, 3, 99); cout << "插入后: "; for (int n : vec) cout << n << " "; cout << endl; cout << "返回值指向: " << *it << endl; cout << "插入了 " << 3 << " 个 " << 99 << endl; return 0; } // 插入前: 1 2 3 4 插入后: 1 99 99 99 2 3 4 返回值指向: 99 插入了 3 个 99
erase:
iterator erase (const_iterator position);//删除单个元素
iterator erase (const_iterator first, const_iterator last);//删除范围元素int main() { vector<int> vec = {10, 20, 30, 40, 50, 60}; cout << "原始: "; for (int n : vec) cout << n << " "; cout << endl; // 1. 删除单个元素(位置2的元素30) vec.erase(vec.begin() + 2); cout << "删除位置2后: "; for (int n : vec) cout << n << " "; cout << endl; // 2. 删除范围元素(位置1到3) vec.erase(vec.begin() + 1, vec.begin() + 3); cout << "删除位置1-3后: "; for (int n : vec) cout << n << " "; cout << endl; return 0; } // 原始: 10 20 30 40 50 60 删除位置2后: 10 20 40 50 60 删除位置1-3后: 10 50 60
swap:
void swap (vector& x);
顾名思义,这个接口的功能就是快速交换两个 vector 的内容。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec1 = {1, 2, 3}; vector<int> vec2 = {10, 20, 30, 40}; cout << "交换前:" << endl; cout << "vec1: "; for (int n : vec1) cout << n << " "; cout << " | size: " << vec1.size() << endl; cout << "vec2: "; for (int n : vec2) cout << n << " "; cout << " | size: " << vec2.size() << endl; // 交换两个vector vec1.swap(vec2); cout << "\n交换后:" << endl; cout << "vec1: "; for (int n : vec1) cout << n << " "; cout << " | size: " << vec1.size() << endl; cout << "vec2: "; for (int n : vec2) cout << n << " "; cout << " | size: " << vec2.size() << endl; return 0; } // 交换前: vec1: 1 2 3 | size: 3 vec2: 10 20 30 40 | size: 4 交换后: vec1: 10 20 30 40 | size: 4 vec2: 1 2 3 | size: 3
更多推荐

所有评论(0)