STL---<vector>
介绍
vector
是STL容器中常用的容器,和数组类似,由于其大小size可变,常用于数组大小不可知的情况下来替代数组;vector
是为了实现动态数组而产生的容器,在取名的时候取了向量
这个名字,但是我觉得还是一个数组;vector
也是一种容器,在 内存中连续排列 ,因此可以通过下标快速访问,时间复杂度为1。然而,连续排列也意味着大小固定,数据超过vector
的预定值时vector
将自动扩容。
创建和使用方法
创建方法
vector
本质是类模板,可以存储任何类型的数据。数组在声明前需要加上数据类型,而vector则通过模板参量设定类型。
c++
#include <iostream>
#include <vector> // 使用vector必须包含的头文件
using namespace std; // 使用标准命名空间,避免每次都要写std::
// 定义一个打印vector内容的函数,方便演示
void printVector(const vector<int>& vec) {
for (size_t i = 0; i < vec.size(); ++i) {
cout << vec[i] << " ";
}
cout << endl;
}
int main() {
// 1. 创建空vector
// 这是最基本的创建方式,创建一个不包含任何元素的vector
vector<int> vec1;
cout << "vec1 (空vector): ";
printVector(vec1); // 输出为空
// 2. 创建指定大小的vector,元素初始化为默认值
// 这里创建包含5个整数的vector,每个元素默认初始化为0
vector<int> vec2(5);
cout << "vec2 (5个默认初始化的元素,默认为0): ";
printVector(vec2); // 输出: 0 0 0 0 0
// 3. 创建指定大小并指定初始值的vector
// 创建包含3个元素的vector,每个元素的初始值都是100
vector<int> vec3(3, 100);
cout << "vec3 (3个元素,初始值均为100): ";
printVector(vec3); // 输出: 100 100 100
// 4. 使用初始化列表创建vector (C++11及以上)
// 直接使用花括号初始化列表来创建并初始化vector
vector<int> vec4 = {1, 2, 3, 4, 5};
cout << "vec4 (初始化列表创建): ";
printVector(vec4); // 输出: 1 2 3 4 5
// 5. 使用数组初始化vector
// 通过指定数组的起始地址和结束地址来初始化vector
int arr[] = {10, 20, 30, 40, 50};
vector<int> vec5(arr, arr + sizeof(arr) / sizeof(arr[0])); // 计算数组长度
cout << "vec5 (通过数组创建): ";
printVector(vec5); // 输出: 10 20 30 40 50
// 6. 使用迭代器范围初始化vector
// 使用另一个vector的迭代器范围来创建新的vector
vector<int> vec6(vec4.begin(), vec4.begin() + 3); // 取vec4的前3个元素
cout << "vec6 (迭代器范围创建): ";
printVector(vec6); // 输出: 1 2 3
// 7. 使用拷贝构造函数创建vector
// 通过另一个vector创建一个完全相同的副本
vector<int> vec7(vec4);
cout << "vec7 (拷贝vec4创建): ";
printVector(vec7); // 输出: 1 2 3 4 5
// 8. 使用赋值运算符创建vector
// 使用等号将另一个vector的内容赋值给新vector
vector<int> vec8 = vec3;
cout << "vec8 (赋值运算符创建): ";
printVector(vec8); // 输出: 100 100 100
return 0;
}
常见使用方法
C++ 中的 std::vector
是一个功能强大的动态数组容器,它提供了丰富的方法来操作和管理数据。下面是一些最常用的方法,我会用表格先汇总核心方法,然后进行详细解释和代码示例。
方法类别 | 方法名 | 功能描述 | 时间复杂度 |
---|---|---|---|
元素访问 | operator[] |
通过索引访问元素,不进行边界检查 | O(1) |
at(size_type pos) |
通过索引访问元素,进行边界检查,越界抛出异常 | O(1) | |
front() |
返回第一个元素的引用 | O(1) | |
back() |
返回最后一个元素的引用 | O(1) | |
data() |
返回指向底层数组的指针 | O(1) | |
容量查询 | size() |
返回当前元素个数 | O(1) |
capacity() |
返回当前已分配的内存容量(可容纳元素个数) | O(1) | |
empty() |
判断容器是否为空 | O(1) | |
修改操作 | push_back(const T&) |
在末尾添加一个元素 | O(1) |
emplace_back(Args...) |
在末尾直接构造一个元素,避免拷贝 | O(1) | |
pop_back() |
删除末尾元素 | O(1) | |
insert(iterator, ...) |
在指定位置插入一个或多个元素 | O(n) | |
erase(iterator) |
删除指定位置的一个或一段元素 | O(n) | |
clear() |
清空所有元素 | O(n) | |
容量管理 | reserve(size_type n) |
预分配至少容纳 n 个元素的内存空间 | - |
resize(size_type n) |
调整容器的大小 | - | |
shrink_to_fit() |
请求移除未使用的容量,减少内存占用 | - | |
迭代器 | begin() / end() |
返回指向第一个元素/尾后位置的迭代器 | O(1) |
rbegin() / rend() |
返回反向迭代器 | O(1) |
🧠 详细说明与代码示例
1. 元素访问
-
operator[]
和at()
都用于通过索引访问元素。at()
会进行边界检查,如果索引越界会抛出std::out_of_range
异常,而operator[]
不进行边界检查,访问越界时行为未定义。c++std::vector<int> vec = {10, 20, 30}; std::cout << vec[1]; // 输出 20 std::cout << vec.at(1); // 输出 20 // std::cout << vec.at(5); // 抛出 std::out_of_range 异常
-
front()
和back()
分别返回首尾元素的引用。c++std::cout << vec.front(); // 输出 10 std::cout << vec.back(); // 输出 30
-
data()
返回指向底层数组的指针,便于与 C 风格函数交互。c++int* p = vec.data(); std::cout << *(p + 1); // 输出 20
2. 容量查询
-
size()
返回当前元素数量,capacity()
返回当前已分配内存可容纳的元素数量,capacity()
通常 >=size()
。 -
empty()
检查容器是否为空。c++if (vec.empty()) { std::cout << "Vector is empty."; }
3. 修改操作
-
push_back()
和emplace_back()
都在末尾添加元素。emplace_back()
直接在容器内构造对象,对于非内置类型效率更高。c++vec.push_back(40); // 添加元素 40 vec.emplace_back(50); // 直接构造元素 50
-
pop_back()
删除末尾元素。c++vec.pop_back(); // 删除 50c++
-
insert()
在指定迭代器位置前插入元素。它可以插入单个元素、多个相同元素、一个迭代器范围或初始化列表。c++// 在第二个元素(索引1)前插入 99 auto it = vec.insert(vec.begin() + 1, 99); // vec: 10, 99, 20, 30, 40 // 在位置 it 前插入 2 个 88 vec.insert(it, 2, 88); // vec: 10, 88, 88, 99, 20, 30, 40
-
erase()
删除指定位置或范围的元素。c++// 删除第三个元素(索引2) vec.erase(vec.begin() + 2); // 删除前两个元素 [vec.begin(), vec.begin()+2) vec.erase(vec.begin(), vec.begin() + 2);
-
clear()
清空所有元素,但不释放容量。c++vec.clear(); // size() 变为 0,capacity() 不变
4. 容量管理
-
reserve(n)
预分配 至少可容纳n
个元素的内存空间,避免多次重新分配,提高性能。c++std::vector<int> vec; vec.reserve(1000); // 提前分配足够空间,避免添加1000个元素时多次扩容 for (int i = 0; i < 1000; ++i) { vec.push_back(i); }
-
resize(n)
调整 容器的大小。如果n > size()
,则在末尾添加默认值或指定值的元素;如果n < size()
,则截断多余元素。c++vec.resize(5); // 如果size<5,则添加默认值0的元素;否则截断 vec.resize(10, 1); // 如果size<10,则添加值为1的元素
-
shrink_to_fit()
请求 释放未使用的内存容量,使capacity()
接近size()
。这是一个非强制性请求,实现可能会忽略。c++vec.shrink_to_fit(); // 希望减少容量至与size匹配
5. 迭代器
迭代器用于遍历容器。
begin()
/end()
:获取指向首元素和尾后元素的正向迭代器。rbegin()
/rend()
:获取反向迭代器,用于逆序遍历。- C++11 起提供了
cbegin()
,cend()
,crbegin()
,crend()
返回常量迭代器。
c++
// 正向遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 反向遍历
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
std::cout << *rit << " ";
}
// 基于范围的for循环 (C++11)
for (const auto& elem : vec) {
std::cout << elem << " ";
}
⚡ 重要注意事项
-
迭代器失效 :在进行修改操作(如
insert
,erase
,push_back
(可能导致扩容))后,指向 vector 的迭代器、指针和引用可能会失效。切记不要在操作后使用旧的迭代器。 -
算法兼容 :
std::vector
可与标准库算法(如std::sort
,std::find
)完美配合。使用#include <algorithm>
。c++std::sort(vec.begin(), vec.end()); // 排序
-
预分配空间 :如果能预估元素数量,使用
reserve()
预先分配空间可以显著提高性能,避 -
免多次扩容和数据拷贝。