C++ STL Vector容器详解
作者:L_兔力
一、vector 简介
1.1 什么是 vector?
vector 是 C++ STL(标准模板库)中的一种动态数组容器。
它本质上是一块连续的动态内存空间,可以:
- 自动扩容
- 随机访问
- 在尾部高效插入删除
- 保存任意类型的数据
例如:
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
return 0;
}此时:
v ┌───┬───┬───┐ │ 1 │ 2 │ 3 │ └───┴───┴───┘
vector 内部保存三个整数。
二、vector 的底层原理
理解 vector 最重要的是理解它的三个成员:
start finish end_of_storage
可以理解为:
capacity空间
start end_of_storage
↓ ↓
┌────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ │ │
└────┴────┴────┴────┴────┘
↑ ↑
finish 可扩容空间三个指针:
| 成员 | 作用 |
|---|---|
| start | 指向数组开始 |
| finish | 指向有效元素末尾 |
| end_of_storage | 指向申请空间末尾 |
size 和 capacity 区别
这是 vector 高频考点。
size()
表示:
当前已经存储多少个元素
例如:
vector<int> v; v.push_back(10); v.push_back(20); cout << v.size();
输出:
2
capacity()
表示:
当前申请了多少内存空间
例如:
vector<int> v; v.push_back(10); cout << v.capacity();
可能输出:
1
也可能:
2
因为不同编译器扩容策略不同。
例如:
vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3);
可能:
size = 3 capacity = 4
内存:
capacity=4 ┌───┬───┬───┬───┐ │ 1 │ 2 │ 3 │ │ └───┴───┴───┴───┘
三、vector 的初始化方式
3.1 默认初始化
vector<int> v;
创建空 vector:
size=0 capacity=0
3.2 指定大小
vector<int> v(5);
创建 5 个元素:
0 0 0 0 0
3.3 指定大小和值
vector<int> v(5,10);
结果:
10 10 10 10 10
3.4 使用数组初始化
int arr[]={1,2,3,4};
vector<int> v(arr,arr+4);结果:
1 2 3 4
3.5 拷贝构造
vector<int> v1{1,2,3};
vector<int> v2(v1);两个 vector 独立:
v1: 1 2 3 v2: 1 2 3
修改 v2:
v2[0]=100;
不会影响 v1。
因为 vector 使用:
深拷贝
四、vector 常用接口大全
4.1 push_back()
尾插元素
vector<int> v; v.push_back(10); v.push_back(20);
结果:
10 20
时间复杂度:
O(1)
平均情况下。
4.2 pop_back()
删除尾部元素
v.pop_back();
例如:
10 20 30
删除:
10 20
注意:
只能删除最后一个。
4.3 size()
获取元素个数
cout<<v.size();
返回:
size_t
4.4 empty()
判断是否为空
if(v.empty())
{
cout<<"空";
}返回:
true / false
4.5 clear()
清空元素
v.clear();
例如:
之前:
1 2 3 4
之后:
空
注意:
clear 不一定释放空间。
例如:
vector<int> v(100); v.clear(); cout<<v.capacity();
可能:
100
4.6 reserve()
提前开空间
vector<int> v; v.reserve(100);
表示:
提前申请100个元素空间。
例如:
没有 reserve:
1 2 4 8 16 32 ...
不断扩容。
使用 reserve:
capacity=100
减少扩容次数。
reserve 注意
vector<int> v; v.reserve(100); cout<<v.size();
输出:
0
因为:
reserve 只改变 capacity。
不会创建元素。
4.7 resize()
改变元素数量
例如:
vector<int> v; v.resize(5);
结果:
0 0 0 0 0
指定值:
v.resize(5,10);
结果:
10 10 10 10 10
区别:
| 函数 | 作用 |
|---|---|
| reserve | 扩容空间 |
| resize | 改变元素数量 |
五、vector 的访问方式
5.1 operator[]
vector<int> v={1,2,3};
cout<<v[0];输出:
1
特点:
不检查越界。
例如:
v[10];
可能崩溃。
5.2 at()
cout<<v.at(0);
特点:
检查越界。
例如:
v.at(10);
抛异常:
out_of_range
5.3 front()
访问第一个元素
cout<<v.front();
5.4 back()
访问最后一个元素
cout<<v.back();
六、vector 迭代器
迭代器是 STL 的核心。
vector 迭代器类似指针。
6.1 begin()
开始位置
vector<int>::iterator it=v.begin();
指向:
第一个元素
6.2 end()
结束位置
v.end();
注意:
end 指向最后一个元素后面。
例如:
1 2 3 ^ ^ | | begin end
遍历 vector
方法1:普通 for
for(size_t i=0;i<v.size();i++)
{
cout<<v[i];
}方法2:迭代器
vector<int>::iterator it=v.begin();
while(it!=v.end())
{
cout<<*it<<" ";
it++;
}方法3:范围 for
C++11:
for(auto e:v)
{
cout<<e;
}七、vector 插入和删除
7.1 insert()
指定位置插入
例如:
vector<int> v={1,2,3};
v.insert(v.begin()+1,100);结果:
1 100 2 3
时间复杂度:
O(N)
因为需要移动元素。
7.2 erase()
删除指定位置
v.erase(v.begin()+1);
例如:
1 2 3
删除2:
1 3
删除区间:
v.erase(v.begin(),v.begin()+2);
删除:
前两个元素
八、vector 扩容机制(重点)
当空间不足:
例如:
capacity=3 1 2 3
继续:
push_back(4);
发生:
- 申请更大的空间
- 拷贝旧数据
- 释放旧空间
- 插入新数据
过程:
旧空间 1 2 3 ↓ 新空间 1 2 3 _ _ _ ↓ 释放旧空间
为什么 vector 扩容不是一点一点增加?
因为:
如果每次增加1:
插入n个元素:
需要大量搬迁。
效率:
O(n²)
所以通常采用:
2倍扩容
达到:
均摊 O(1)
九、vector 的深浅拷贝
vector 内部:
保存:
指针 数据 容量
如果浅拷贝:
v1 ↓ 同一块空间 ↑ v2
会造成:
- 修改互相影响
- double free
所以 vector 使用:
深拷贝
例如:
vector<int> v1={1,2,3};
vector<int> v2=v1;内存:
v1: 1 2 3 v2: 1 2 3
两个空间独立。
十、vector 失效问题(面试重点)
10.1 扩容导致迭代器失效
vector<int> v={1,2,3};
auto it=v.begin();
v.push_back(4);
cout<<*it;可能错误。
原因:
扩容后:
旧空间释放。
原 iterator 指向:
已经释放的地址
10.2 erase导致迭代器失效
auto it=v.begin(); v.erase(it);
之后:
it 不可使用。
十一、vector 与数组区别
| 数组 | vector | |
|---|---|---|
| 大小 | 固定 | 动态 |
| 内存 | 栈区 | 堆区 |
| 扩容 | 不能 | 可以 |
| 效率 | 高 | 略低 |
| 安全性 | 低 | 高 |
十二、vector 与 list 区别
| vector | list | |
|---|---|---|
| 底层 | 连续数组 | 链表 |
| 随机访问 | 快 | 慢 |
| 插入删除 | 中间慢 | 快 |
| 缓存友好 | 好 | 差 |
十三、vector 性能分析
| 操作 | 复杂度 |
|---|---|
| 随机访问 | O(1) |
| 尾插 | O(1) |
| 尾删 | O(1) |
| 头插 | O(N) |
| 中间插入 | O(N) |
| 查找 | O(N) |
十四、vector 模拟实现(核心思想)
简单版本:
template<class T>
class Vector
{
public:
Vector()
{
_start=nullptr;
_finish=nullptr;
_end=nullptr;
}
private:
T* _start;
T* _finish;
T* _end;
};三个指针:
_start
↓
[1][2][3][ ][ ]
↑
finish
↑
end这就是 STL vector 的核心结构。
十五、vector 高频面试题
1.vector 为什么支持随机访问?
因为:
底层是连续数组。
可以:
地址=首地址+下标*sizeof(type)
2.vector 为什么尾插效率高?
因为:
尾部空间直接写入。
3.vector 扩容为什么消耗大?
因为:
需要:
- 申请新空间
- 拷贝数据
- 释放旧空间
4.reserve 和 resize区别?
答案:
reserve: 改变容量 resize: 改变元素数量
5.vector 如何释放空间?
方法:
vector<int>().swap(v);
原理:
创建临时空 vector
交换资源。
总结
vector 是 C++ STL 中最重要的容器之一。
核心知识:
- vector 是动态数组
- 底层是一块连续空间
- 使用三个指针管理数据
- size 表示元素数量
- capacity 表示容量
- 扩容会导致迭代器失效
- 尾插效率高
- 中间插入删除效率低
- 支持随机访问
- STL 中大量算法基于 vector
到此这篇关于C++ STL vector容器详解的文章就介绍到这了,更多相关C++ STL 容器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
