.
💓 博客主页:倔强的石头的CSDN主页
📝Gitee主页:倔强的石头的gitee主页
⏩ 文章专栏:《C++指南》
期待您的关注![]()
std::vector
是 C++ STL 中最核心的动态数组容器,支持高效随机访问和动态扩容。本文从 基础用法 和 深度优化 两个维度,系统解析其构造函数、迭代器、容量管理、元素访问和修改操作,涵盖函数重载、参数差异及相似函数对比,并提供丰富的代码示例。
一、默认成员函数
1. 默认构造函数
- 语法:
vector<T> vec;
- 行为:创建空容器,容量为 0,不分配内存。
- 示例:
std::vector<int> vec; // 空 vector
2. 元素数量构造函数
- 语法:
vector<T> vec(n, val);
- 行为:
vector<T> vec(n)
:创建包含n
个默认初始化元素的容器(如int
初始化为 0)。vector<T> vec(n, val)
:创建包含n
个值为val
的元素的容器。
- 示例:
std::vector<int> vec1(5); // {0, 0, 0, 0, 0} std::vector<int> vec2(3, 10); // {10, 10, 10}
3. 迭代器范围构造函数
- 语法:
vector<T> vec(iter_start, iter_end);
- 行为:用其他容器的迭代器范围初始化
vector
。 - 示例:
std::list<int> lst = {1, 2, 3}; std::vector<int> vec(lst.begin(), lst.end()); // {1, 2, 3}
4. 初始化列表构造函数(C++11 起)
- 语法:
vector<T> vec{1, 2, 3};
- 行为:直接通过列表初始化元素。
- 示例:
std::vector<int> vec = {4, 5, 6}; // {4, 5, 6}
5. 拷贝构造函数与赋值运算符
- 语法:
vector<T> vec2(vec1); // 拷贝构造 vec2 = vec1; // 赋值运算符
- 行为:深拷贝容器内容,新旧容器完全独立。
- 示例:
std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2(vec1); // 拷贝构造 vec2 = vec1; // 赋值操作
二、迭代器相关函数
1. 基础迭代器
函数 | 功能 | 示例 |
---|---|---|
begin() |
返回指向第一个元素的迭代器 | auto it = vec.begin(); |
end() |
返回指向末尾(最后一个元素之后)的迭代器 | for (auto it = vec.begin(); it != vec.end(); ++it) |
rbegin() |
返回反向迭代器(从末尾开始遍历) | auto rit = vec.rbegin(); |
rend() |
返回反向迭代器的结束位置 | while (rit != vec.rend()) |
2. 常量迭代器(C++11 起)
函数 | 功能 | 示例 |
---|---|---|
cbegin() |
返回常量正向迭代器 | auto cit = vec.cbegin(); |
cend() |
返回常量正向迭代器的结束位置 | for (; cit != vec.cend(); ++cit) |
crbegin() |
返回常量反向迭代器 | auto crit = vec.crbegin(); |
crend() |
返回常量反向迭代器的结束位置 | while (crit != vec.crend()) |
示例:
std::vector<int> vec = {10, 20, 30};
// 正向遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // 输出:10 20 30
}
// 常量反向遍历
for (auto crit = vec.crbegin(); crit != vec.crend(); ++crit) {
std::cout << *crit << " "; // 输出:30 20 10
}
三、容量管理函数
1. 容量与大小控制
函数 | 功能 | 示例 |
---|---|---|
size() |
返回当前元素数量 | int n = vec.size(); |
capacity() |
返回已分配的内存容量 | int cap = vec.capacity(); |
empty() |
检查容器是否为空 | if (vec.empty()) { ... } |
2. 动态调整函数
resize(n, val)
vs resize(n)
- 语法:
void resize(size_t n); // 默认值填充 void resize(size_t n, T val); // 指定值填充
- 行为差异:
resize(5)
:若原大小为 3,新增 2 个默认值元素(如int
为 0)。resize(5, 10)
:新增的 2 个元素值为 10。- 若
n < size()
,超出部分的元素被销毁,但 容量不变。 - 若
n>capacity()
,将会引发扩容
示例:
std::vector<int> vec = {1, 2, 3};
vec.resize(5); // {1, 2, 3, 0, 0}
vec.resize(3); // {1, 2, 3}(容量仍可能为 5)
vec.resize(5, 10); // {1, 2, 3, 10, 10}
reserve(n)
vs shrink_to_fit()
reserve(n)
:预分配至少n
个元素的内存,避免频繁扩容。shrink_to_fit()
:请求释放未使用的内存,但 不保证容量等于size()
。 该函数谨慎使用 因为动态申请的内存不支持分段释放,缩容实际上是开辟了新的空间,释放了原有的空间,会有一定的效率牺牲
示例:
std::vector<int> vec;
vec.reserve(100); // 容量为 100
vec.push_back(1);
vec.shrink_to_fit(); // 容量可能变为 1(具体由实现决定)
四、元素访问函数
1. 下标访问与安全访问
特性 | operator[] |
at() |
---|---|---|
越界检查 | assert断言 | 有(抛出 std::out_of_range ) |
性能 | 更高(直接访问) | 略低(需检查) |
适用场景 | 已知索引安全时 | 需要异常处理的场景 |
示例:
std::vector<int> vec = {10, 20, 30};
int a = vec[3]; // 未定义行为(可能崩溃或返回垃圾值)
int b = vec.at(3); // 抛出异常:std::out_of_range
2. 首尾元素访问
函数 | 功能 | 示例 |
---|---|---|
front() |
访问第一个元素 | vec.front() = 5; |
back() |
访问最后一个元素 | vec.back() = 10; |
对比 front()
和 begin()
:
front()
返回元素引用,begin()
返回迭代器。
std::vector<int> vec = {1, 2, 3};
vec.front() = 10; // 直接修改首元素
auto it = vec.begin();
*it = 20; // 通过迭代器修改
3. 底层数据指针 data()
- 语法:
T* data();
- 行为:返回指向底层数组的指针(C++11 起支持)。
- 示例:
std::vector<int> vec = {1, 2, 3}; int* arr = vec.data(); // 获取指针 arr[0] = 10; // 直接修改元素
五、修改操作函数
1. 尾部操作
函数 | 功能 | 示例 |
---|---|---|
push_back(val) |
在末尾添加元素(可能触发拷贝/移动) | vec.push_back(4); |
emplace_back(args) |
直接在末尾构造元素(避免临时对象) | vec.emplace_back(1, 2.0); |
pop_back() |
删除末尾元素 | vec.pop_back(); |
push_back
vs emplace_back
:
push_back
:需构造临时对象,再拷贝或移动到容器。emplace_back
:直接通过参数在容器内构造对象,效率更高。
示例:
class Data {
public:
Data(int a, double b) { /* ... */ }
};
std::vector<Data> vec;
vec.push_back(Data(1, 2.0)); // 构造临时对象,再移动
vec.emplace_back(1, 2.0); // 直接在容器内构造
2. 插入与删除
insert
的多个重载版本
- 语法:
iterator insert(iterator pos, const T& val); // 插入单个元素 void insert(iterator pos, size_t n, const T& val); // 插入 n 个相同元素 void insert(iterator pos, InputIt first, InputIt last); // 插入迭代器范围 //此范围包含 first 指向的元素,但不包含 last 指向的元素
- 示例:
std::vector<int> vec = {1, 2, 3}; vec.insert(vec.begin() + 1, 5); // {1, 5, 2, 3} vec.insert(vec.begin(), 2, 10); // {10, 10, 1, 5, 2, 3} std::list<int> lst = {7, 8}; vec.insert(vec.end(), lst.begin(), lst.end()); // 末尾追加 7, 8
erase
的用法
- 语法:
iterator erase(iterator pos); // 删除单个元素 iterator erase(iterator first, iterator last); // 删除范围元素
- 示例:
std::vector<int> vec = {1, 2, 3, 4, 5}; vec.erase(vec.begin()); // {2, 3, 4, 5} vec.erase(vec.begin(), vec.begin() + 2); // {4, 5}
3. 内容替换与清空
函数 | 功能 | 示例 |
---|---|---|
assign |
替换容器内容 | vec.assign(3, 5); // {5,5,5} |
clear() |
清空所有元素(容量不变) | vec.clear(); |
swap |
交换两个容器的内容 | vec1.swap(vec2); |
assign
的重载版本:
std::vector<int> vec;
vec.assign(3, 5); // {5, 5, 5}(填充 n 个值)
vec.assign({1, 2, 3}); // {1, 2, 3}(初始化列表)
std::list<int> lst = {7, 8};
vec.assign(lst.begin(), lst.end()); // {7, 8}(迭代器范围)
六、使用建议和注意事项
- 构造函数选择:
- 默认构造用
vector<T> vec;
,预分配内存用vector(n)
,列表初始化用{}
。
- 默认构造用
- 容量管理:
- 频繁添加元素时优先
reserve()
,可以减少频繁扩容的效率降低
- 频繁添加元素时优先
- 元素访问:
- 安全场景用
operator[]
,需异常处理时用at()
。
- 安全场景用
- 修改操作:
- 优先
emplace_back
减少拷贝,批量插入用insert
的重载版本。
- 优先
- 迭代器使用:
- 常量遍历用
cbegin()/cend()
,反向遍历用rbegin()/rend()
。
- 常量遍历用
通过合理选择函数重载和参数,可以显著提升代码的效率和健壮性。
本文完
下篇文章将为读者讲解vector的底层原理和模拟实现