C++ 容器类 vector
C++ 容器类 中文讲解
关键要点
std::vector
是 C++ 标准模板库(STL)中的一种动态数组容器,研究表明它是处理可变大小数据集合的首选工具。- 它支持动态调整大小、随机访问和高效的尾部操作(如插入和删除),适合需要灵活存储的场景。
- 提供丰富的成员函数,如
push_back
、pop_back
、size
等,便于操作和遍历。 - 相比 C 风格数组,
std::vector
更安全且易于管理内存;相比std::list
,它提供更快的随机访问。
什么是 std::vector
?
std::vector
是一个动态数组容器,允许在运行时添加或删除元素。它就像一个可以自动扩展的数组,能够存储任意类型的对象,比如整数、字符串或自定义结构体。你可以把它想象成一个智能的数组,会自己管理内存,减少手动分配和释放的麻烦。
基本用法
要使用 std::vector
,需要包含 <vector>
头文件。以下是一个简单示例,展示如何创建和操作向量:
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3}; // 初始化一个包含 1, 2, 3 的向量
vec.push_back(4); // 在末尾添加 4
for (const auto& num : vec) {
std::cout << num << " "; // 输出: 1 2 3 4
}
std::cout << std::endl;
return 0;
}
主要功能
- 动态扩展:可以随时添加或删除元素,自动调整内存。
- 快速访问:通过下标(如
vec[0]
)快速访问元素。 - 丰富接口:支持插入、删除、遍历等操作,兼容 STL 算法。
适用场景
std::vector
适合需要存储可变数量元素的场景,比如记录用户输入、存储动态数据或作为算法的输入数据。如果需要频繁在中间插入或删除元素,可能需要考虑其他容器如 std::list
。
详细报告
本文基于 2025 年 7 月 11 日的最新网络资源,全面解读 C++ 标准库 <vector>
的用法、特性和最佳实践,旨在为开发者提供清晰的指导。
背景与概述
在 C++ 编程中,管理动态数据集合是常见需求。传统 C 风格数组大小固定,无法动态调整,且容易发生越界访问问题。C++ 标准模板库(STL)中的 std::vector
是一个动态数组容器,自 C++98 起引入,提供了灵活的大小调整、随机访问和自动内存管理功能。研究表明,std::vector
是 C++ 中最常用的容器之一,广泛应用于需要动态存储的场景。
<vector>
的位置和作用
- 文件位置:
<vector>
是 C++ 标准库的一部分,通常由编译器提供,无需额外安装。 - 主要作用:定义
std::vector
容器,用于存储可变数量的元素,支持动态扩展和高效的随机访问。
std::vector
的定义与特性
std::vector
是一个模板类,定义如下:
template<class T, class Allocator = std::allocator<T>> class vector;
- T:元素类型,可以是基本类型(如
int
、double
)、字符串(std::string
)或自定义结构体。 - Allocator:内存分配器,默认使用
std::allocator
。
特性:
- 动态大小:可以根据需要自动增长或缩小。
- 连续内存:元素存储在连续的内存块中,支持 O(1) 的随机访问。
- 高效尾部操作:在末尾插入(
push_back
)或删除(pop_back
)的复杂度为 O(1)。 - 标准容器接口:支持迭代器、STL 算法(如
std::sort
、std::find
)。
std::vector
的基本用法
要使用 std::vector
,需包含 <vector>
头文件,并使用 std
命名空间:
#include <vector>
using namespace std; // 或使用 std::vector
创建与初始化
std::vector
支持多种创建方式:
- 空向量:
vector<int> vec; // 创建一个空的整数向量
- 指定大小:
vector<double> vec(10); // 创建大小为 10 的向量,元素默认初始化为 0.0
vector<double> vec(10, 1.0); // 创建大小为 10 的向量,元素初始化为 1.0
- 初始化列表(C++11 起):
vector<int> vec = {1, 2, 3, 4, 5}; // 初始化为 1, 2, 3, 4, 5
- 从数组复制:
int arr[] = {1, 2, 3};
vector<int> vec(arr, arr + 3); // 复制数组的前 3 个元素
- 拷贝构造:
vector<int> vec1 = {1, 2, 3};
vector<int> vec2(vec1); // vec2 是 vec1 的拷贝
成员函数
std::vector
提供了丰富的成员函数,涵盖元素访问、修改、容量管理和迭代器操作。以下是主要成员函数的分类和功能:
类别 | 函数 | 功能 |
---|---|---|
构造 | vector() | 创建空向量。 |
vector(size_t n) | 创建大小为 n 的向量,元素默认初始化。 | |
vector(size_t n, const T& val) | 创建大小为 n 的向量,元素初始化为 val 。 | |
vector(const vector&) | 拷贝构造。 | |
vector(iterator first, iterator last) | 从迭代器范围 [first, last) 构造。 | |
访问 | at(size_t pos) | 返回位置 pos 的元素,带越界检查。 |
operator[](size_t pos) | 返回位置 pos 的元素,无越界检查。 | |
front() | 返回第一个元素。 | |
back() | 返回最后一个元素。 | |
data() | 返回指向第一个元素的指针。 | |
修改 | push_back(const T& x) | 在末尾添加元素 x 。 |
pop_back() | 删除最后一个元素。 | |
insert(iterator pos, const T& x) | 在 pos 插入元素 x 。 | |
erase(iterator pos) | 删除 pos 处的元素。 | |
clear() | 清空所有元素。 | |
resize(size_t n) | 调整大小为 n ,新元素默认初始化。 | |
resize(size_t n, const T& val) | 调整大小为 n ,新元素初始化为 val 。 | |
assign(size_t n, const T& val) | 将向量设置为 n 个 val 。 | |
assign(iterator first, iterator last) | 将向量设置为范围 [first, last) 。 | |
emplace(iterator pos, Args&&... args) | 在 pos 构造元素(C++11)。 | |
emplace_back(Args&&... args) | 在末尾构造元素(C++11)。 | |
容量 | size() | 返回当前元素数量。 |
capacity() | 返回当前分配的容量。 | |
max_size() | 返回最大可能元素数量。 | |
reserve(size_t n) | 预留至少 n 个元素的容量。 | |
shrink_to_fit() | 减少容量到当前大小(C++11)。 | |
迭代器 | begin() | 返回指向第一个元素的迭代器。 |
end() | 返回指向最后一个元素后位置的迭代器。 | |
rbegin() | 返回指向最后一个元素的反向迭代器。 | |
rend() | 返回指向第一个元素前位置的反向迭代器。 |
示例代码
以下是一个综合示例,展示 std::vector
的常见操作:
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
// 初始化
std::vector<int> vec = {1, 2, 3};
// 添加元素
vec.push_back(4); // 添加 4
vec.emplace_back(5); // 构造并添加 5
// 插入元素
vec.insert(vec.begin() + 1, 10); // 在索引 1 插入 10
// 删除元素
vec.erase(vec.begin() + 2); // 删除索引 2 的元素
vec.pop_back(); // 删除最后一个元素
// 遍历
std::cout << "向量内容: ";
for (const auto& num : vec) {
std::cout << num << " "; // 输出: 1 10 2 4
}
std::cout << std::endl;
// 排序
std::sort(vec.begin(), vec.end());
std::cout << "排序后: ";
for (const auto& num : vec) {
std::cout << num << " "; // 输出: 1 2 4 10
}
std::cout << std::endl;
// 容量管理
std::cout << "大小: " << vec.size() << ", 容量: " << vec.capacity() << std::endl;
vec.reserve(10); // 预留容量
std::cout << "预留后容量: " << vec.capacity() << std::endl;
return 0;
}
性能分析
- 随机访问:
operator[]
和at()
的复杂度为 O(1)。 - 尾部操作:
push_back
和pop_back
的平均复杂度为 O(1),但扩容可能导致 O(n)。 - 中间插入/删除:
insert
和erase
的复杂度为 O(n),因为需要移动后续元素。 - 扩容机制:当
size
超过capacity
时,std::vector
会重新分配内存,通常容量翻倍(或 1.5 倍,视实现而定),导致元素拷贝。
与其他容器的比较
特性 | std::vector | std::array | std::list |
---|---|---|---|
大小调整 | 动态调整 | 固定大小 | 动态调整 |
内存布局 | 连续内存 | 连续内存 | 非连续(链表) |
随机访问 | O(1) | O(1) | O(n) |
插入/删除 | 尾部 O(1),中间 O(n) | 不支持 | 任意位置 O(1) |
内存管理 | 自动管理,需注意扩容 | 无动态分配 | 自动管理,无扩容问题 |
高级特性
- C++11 特性:
- 初始化列表:支持
vector<int> vec = {1, 2, 3};
。 - emplace 系列函数:
emplace
和emplace_back
直接构造元素,减少拷贝开销。 - shrink_to_fit:优化内存使用。
- 迭代器失效:当向量重新分配内存(如
push_back
导致扩容)时,所有迭代器、指针和引用都会失效。 - 自定义类型:
std::vector
可以存储自定义结构体,但需确保类型支持拷贝或移动操作。例如:
struct Rect {
int id, length, width;
bool operator<(const Rect& other) const {
if (id != other.id) return id < other.id;
if (length != other.length) return length < other.length;
return width < other.width;
}
};
vector<Rect> vec;
最佳实践
- 预留容量:使用
reserve(n)
预分配内存,减少扩容次数。 - 使用
emplace_back
:优先使用emplace_back
而非push_back
,以减少构造和拷贝开销。 - 避免频繁中间操作:如果需要频繁在中间插入或删除,考虑
std::list
或std::deque
。 - 检查越界:使用
at()
而非[]
来访问元素,以避免未定义行为。 - 注意迭代器失效:在修改向量后(如
push_back
、insert
),重新获取迭代器。
常见问题与解决方案
- 越界访问:使用
at()
代替[]
以启用越界检查。 - 扩容性能问题:提前使用
reserve()
预留足够容量。 - 迭代器失效:在可能导致扩容的操作后,重新获取迭代器。
- 内存浪费:使用
shrink_to_fit()
减少多余容量。
总结与建议
std::vector
是 C++ 中处理动态数组的理想选择,结合了高效的随机访问和灵活的内存管理。通过本文的讲解,您应该能够掌握 std::vector
的基本用法、特性及最佳实践。建议在实际开发中根据需求选择合适的容器,并结合 STL 算法提升功能性。更多详细信息可参考以下资源:
开发者可根据具体需求进一步查阅官方文档或上述链接以获取更多细节。