C++ std::vector 基础与遍历#
std::vector 是 C++ 标准库中的动态数组,它允许在运行时调整大小,元素在内存中连续存放。
std::vector 核心知识#
- 头文件:
#include <vector> - 定义:
std::vector<int> v1;// 空vectorstd::vector<int> v2 = {1, 2, 3};// 初始化 (C++11 列表初始化)std::vector<int> v3(10);// 10个元素,默认初始化为0 (对于基本类型)std::vector<int> v4(5, 100);// 5个元素,都为100
- 主要特性:
- 动态大小: 自动管理内存,可增删元素。
- 连续存储: 元素内存连续,支持 O(1) 随机访问。
- 高效末尾操作:
push_back(),pop_back()通常 O(1) 摊还时间。 - 低效中间操作:
insert(),erase()通常 O(N)。
- 常用成员函数:
push_back(val): 末尾添加元素。pop_back(): 删除末尾元素。size(): 元素数量 (类型为size_t)。empty(): 是否为空。clear(): 清空所有元素。at(idx): 访问元素(带边界检查,抛出std::out_of_range异常,安全)。operator[](idx): 访问元素(不带边界检查,速度快,不安全)。front(): 第一个元素。back(): 最后一个元素。begin(),end(): 获取正向迭代器。rbegin(),rend(): 获取反向迭代器。cbegin(),cend(),crbegin(),crend(): 获取常量迭代器 (C++11+)。
嵌套 std::vector (例如,2D 数组)#
嵌套的 std::vector 常用于表示多维数据结构,例如 2D 数组(矩阵)。std::vector<std::vector<int>> 的使用方式与普通 vector 类似,只是多了一层维度。
- 访问元素:
matrix[row_index][col_index] - 获取行数:
matrix.size() - 获取列数 (假定所有行等长):
matrix[0].size()(请先检查matrix.empty()和matrix[0].empty()以防越界)
#include <iostream>
#include <vector>
void printAndModifyMatrix(std::vector<std::vector<int>>& m) {
std::cout << "--- 原始矩阵 ---" << std::endl;
// 遍历并打印
for (const auto& row : m) { // row 是 const std::vector<int>&
for (int val : row) { // val 是 int
std::cout << val << " ";
}
std::cout << std::endl;
}
// 修改第一个元素
if (!m.empty() && !m[0].empty()) {
m[0][0] = 99;
std::cout << "\n--- 修改后矩阵 ---" << std::endl;
for (const auto& row : m) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
}
int main() {
std::vector<std::vector<int>> myMatrix = {{1, 2, 3}, {4, 5, 6}};
printAndModifyMatrix(myMatrix); // 传入引用,允许函数修改原矩阵
return 0;
}
/*
Output:
--- 原始矩阵 ---
1 2 3
4 5 6
--- 修改后矩阵 ---
99 2 3
4 5 6
*/std::vector 遍历方式#
1. 基于范围的 for 循环 (Range-based for loop) - 推荐 (C++11+)#
特点: 最简洁、安全,适用于绝大多数只读或元素独立的修改场景。不能直接获取索引,不应在循环内修改容器大小(添加或删除元素)。
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {10, 20, 30};
// 只读访问
std::cout << "只读访问: ";
for (int num : numbers) { // num 是 numbers 中元素的副本
std::cout << num << " "; // 10 20 30
}
std::cout << std::endl;
// 修改元素
std::cout << "修改后: ";
for (int& num : numbers) { // 使用引用 'int&' 才能修改原元素
num *= 2;
}
for (int num : numbers) {
std::cout << num << " "; // 20 40 60
}
std::cout << std::endl;
return 0;
}2. 基于索引的循环 (Index-based loop)#
特点: 传统方式,当需要知道元素索引时使用,或者需要跳跃访问时。
#include <iostream>
#include <vector>
#include <string> // For std::string
int main() {
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
for (size_t i = 0; i < names.size(); ++i) { // 推荐使用 size_t 类型作为索引
std::cout << "Name at " << i << ": " << names[i] << std::endl;
}
return 0;
}3. 使用迭代器 (Iterator-based loop)#
特点: 最通用、最灵活,适用于所有标准容器。可进行复杂操作(如循环内安全地插入/删除元素),但需注意迭代器失效问题。
#include <iostream>
#include <vector>
int main() {
std::vector<double> scores = {1.1, 2.2, 3.3};
// 传统迭代器遍历
std::cout << "传统迭代器: ";
for (std::vector<double>::iterator it = scores.begin(); it != scores.end(); ++it) {
std::cout << *it << " "; // 解引用迭代器获取元素
}
std::cout << std::endl;
// 使用 auto 简化 (C++11+)
// cbegin()/cend() 返回 const_iterator,用于只读访问,更安全。
std::cout << "使用 auto 和 const 迭代器: ";
for (auto it = scores.cbegin(); it != scores.cend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}4. 使用反向迭代器 (Reverse Iterator)#
特点: 从 vector 末尾向前遍历。
#include <iostream>
#include <vector>
int main() {
std::vector<int> data = {1, 2, 3};
std::cout << "反向遍历: ";
for (auto rit = data.rbegin(); rit != data.rend(); ++rit) { // rbegin()/rend() 返回反向迭代器
std::cout << *rit << " "; // 3 2 1
}
std::cout << std::endl;
return 0;
}推荐使用场景总结:
- 日常遍历 (读或独立修改): 基于范围的
for循环 (最简洁、安全)。 - 需要元素索引: 基于索引的
for循环。 - 复杂操作/通用性 (例如循环中增删元素): 迭代器循环 (需谨慎处理迭代器失效)。
