Skip to content

Ch 14: std::vector 详解

  • 熟练使用 std::vector 的各种构造和初始化方式
  • 掌握迭代器的使用和运算
  • 理解 vector 的容量管理和内存分配策略
  • 学会避免迭代器失效的常见陷阱
  • 理解 emplace vs push 的性能差异
# Python - 动态列表,操作直观
numbers = [1, 2, 3, 4, 5]
# 添加元素
numbers.append(6) # [1, 2, 3, 4, 5, 6]
numbers.insert(0, 0) # [0, 1, 2, 3, 4, 5, 6]
numbers.extend([7, 8]) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
# 访问 - 切片是 Python 特有功能
x = numbers[0] # 第一个
y = numbers[-1] # 最后一个
z = numbers[2:5] # 切片 [3, 4, 5]
sliced = numbers[::2] # 步长 [0, 2, 4, 6, 8]
# 删除
numbers.pop() # 删除最后一个,返回值
numbers.pop(0) # 删除第一个
del numbers[3] # 删除指定位置
numbers.remove(3) # 删除第一个出现的值
# 列表推导
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
#include <vector>
#include <iostream>
int main() {
// 构造方式
std::vector<int> v1; // 空 vector
std::vector<int> v2(5); // 5 个元素,值初始化为 0
std::vector<int> v3(5, 10); // 5 个元素,每个都是 10
std::vector<int> v4 = {1, 2, 3, 4, 5}; // 初始化列表
std::vector<int> v5(v4); // 拷贝构造
std::vector<int> v6(std::move(v4)); // 移动构造
// 添加元素
v1.push_back(1);
v1.push_back(2);
v1.emplace_back(3); // C++11 原位构造,更高效
// 访问
int x = v1[0]; // 第一个(不检查边界)
int y = v1.at(0); // 第一个(带边界检查)
int z = v1.front(); // 第一个
int w = v1.back(); // 最后一个
// 大小
std::size_t size = v1.size(); // 元素个数
bool empty = v1.empty(); // 是否为空
// 删除
v1.pop_back(); // 删除最后一个
v1.clear(); // 清空所有
return 0;
}
特性Python listC++ vector
切片操作list[1:3]需要用 vector + erase 或 std::vector<int> 子范围
负索引list[-1]用 v.back() 或 v[v.size()-1]
自动扩容是是(但需要手动调用 reserve 优化)
类型检查运行时编译时
内存管理全自动半自动(自动析构,手动 shrink_to_fit)
#include <vector>
#include <iostream>
#include <string>
int main() {
// 1. 默认构造 - 空 vector
std::vector<int> empty;
std::cout << "empty.size() = " << empty.size() << std::endl; // 0
// 2. 构造 n 个元素(值初始化)
std::vector<int> five_zeros(5); // {0, 0, 0, 0, 0}
std::vector<std::string> five_empty_strings(5); // 5 个空 string
// 3. 构造 n 个相同元素
std::vector<int> five_tens(5, 10); // {10, 10, 10, 10, 10}
std::vector<std::string> five_hellos(3, "hello"); // {"hello", "hello", "hello"}
// 4. 初始化列表(C++11)
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<std::string> words = {"apple", "banana", "cherry"};
// 5. 从另一个 vector 拷贝
std::vector<int> copy(numbers); // 完整拷贝
std::vector<int> copy2 = numbers; // 同上
// 6. 从迭代器范围构造(C++11)
std::vector<int> from_range(numbers.begin(), numbers.begin() + 3); // {1, 2, 3}
// 7. 移动构造(C++11)
std::vector<int> source = {1, 2, 3};
std::vector<int> moved(std::move(source)); // source 变为空
// source 现在是空 vector!
// 8. C++17 从数组
int arr[] = {1, 2, 3, 4, 5};
std::vector<int> from_array(std::begin(arr), std::end(arr));
// 9. 构造并指定分配器(很少用)
std::vector<int, std::allocator<int>> with_alloc;
return 0;
}
#include <vector>
int main() {
// 列表初始化(花括号)- 优先使用
std::vector<int> v1 = {1, 2, 3}; // 列表初始化
std::vector<int> v2{1, 2, 3}; // 同上(但注意窄化转换)
// 注意:窄化转换问题
double d = 3.14;
// std::vector<int> v3{d}; // ❌ 错误!double 不能窄化为 int
std::vector<int> v3{static_cast<int>(d)}; // OK
// 圆括号构造
std::vector<int> v4(5, 10); // {10, 10, 10, 10, 10}
std::vector<int> v5(5); // {0, 0, 0, 0, 0} - 值初始化
// 陷阱:区分 1 个元素和 1, 2, 3
std::vector<int> v6{10}; // 1 个元素:10
std::vector<int> v7(10); // 10 个元素:0, 0, 0, ...(值初始化)
return 0;
}
#include <vector>
#include <iostream>
#include <stdexcept>
int main() {
std::vector<int> v = {10, 20, 30, 40, 50};
// 1. 下标访问(不检查边界,最快)
int first = v[0]; // 10
int last = v[v.size() - 1]; // 50
// v[100] // ❌ 未定义行为!
// 2. at() 访问(带边界检查)
int second = v.at(1); // 20
// v.at(100) // ❌ 抛出 std::out_of_range
// 3. front() 和 back()
int& front = v.front(); // 10
int& back = v.back(); // 50
front = 100; // v 现在是 {100, 20, 30, 40, 50}
// 4. data() - 返回底层数组指针
int* ptr = v.data();
std::cout << "First element: " << *ptr << std::endl; // 100
// 5. 迭代器访问(见下一节)
auto it = v.begin();
int third = *(it + 2); // 30
// 安全检查
if (!v.empty()) {
// 此时可以安全访问
std::cout << v.front() << " " << v.back() << std::endl;
}
return 0;
}
# Python - 切片
numbers = [10, 20, 30, 40, 50]
subset = numbers[1:4] # [20, 30, 40]
reversed = numbers[::-1] # [50, 40, 30, 20, 10]
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> v = {10, 20, 30, 40, 50};
// C++ - 没有内置切片,需要用算法
// 取子范围 [1:4]
std::vector<int> subset(v.begin() + 1, v.begin() + 4); // {20, 30, 40}
// 反向(手动)
std::vector<int> reversed(v.rbegin(), v.rend()); // {50, 40, 30, 20, 10}
// C++20 可以用 views
#include <ranges>
auto view = v | std::views::drop(1) | std::views::take(3); // {20, 30, 40}
return 0;
}
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {10, 20, 30, 40, 50};
// begin() 和 end()
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " "; // 10 20 30 40 50
}
std::cout << std::endl;
// cbegin() 和 cend() - const 迭代器
for (auto it = v.cbegin(); it != v.cend(); ++it) {
// *it 是 const,不能修改
std::cout << *it << " ";
}
std::cout << std::endl;
// rbegin() 和 rend() - 反向迭代器
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " "; // 50 40 30 20 10
}
std::cout << std::endl;
// crbegin() 和 crend() - const 反向迭代器
for (auto it = v.crbegin(); it != v.crend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {10, 20, 30, 40, 50};
auto it = v.begin(); // 指向 10
// 前进/后退
auto it2 = it + 2; // 指向 30
auto it3 = it2 - 1; // 指向 20
// 距离
std::ptrdiff_t dist = v.end() - v.begin(); // 5
// 比较
if (it2 > it) std::cout << "it2 is after it\n";
// 解引用
int val = *it2; // 30
int val2 = it2[2]; // 50(偏移 2)
int val3 = *(it2 + 2); // 50(等价于 it2[2])
// += 和 -=
auto it4 = v.begin();
it4 += 3; // 指向 40
it4 -= 2; // 指向 20
// 前置/后置递增
auto it5 = v.begin();
++it5; // 前置递增(推荐,更高效)
it5++; // 后置递增
return 0;
}
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// ❌ 危险:添加元素后迭代器可能失效
auto it = v.begin() + 2; // 指向 3
std::cout << "Before insert: *it = " << *it << std::endl;
v.insert(v.begin(), 0); // 插入可能导致重新分配
// it 现在是悬空迭代器!使用它是未定义行为!
// std::cout << "After insert: *it = " << *it << std::endl; // ❌
// ✅ 正确做法:在修改后重新获取迭代器
it = v.begin() + 3; // 重新获取
std::cout << "After re-get: *it = " << *it << std::endl; // 3
// ❌ 危险:删除元素后
it = v.begin() + 2; // 指向 3
v.erase(v.begin()); // 删除第一个元素
// it 仍然指向原来的内存位置,但数据可能已移动
// std::cout << *it << std::endl; // ❌ 危险!
return 0;
}
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// ✅ 安全方式 1:用索引代替迭代器
for (std::size_t i = 0; i < v.size(); ++i) {
if (v[i] == 3) {
v.push_back(100); // 安全(不会在循环中触发 realloc)
}
}
// ✅ 安全方式 2:先记录要修改的位置
std::vector<std::size_t> positions_to_modify;
for (std::size_t i = 0; i < v.size(); ++i) {
if (v[i] % 2 == 0) {
positions_to_modify.push_back(i);
}
}
for (std::size_t idx : positions_to_modify) {
v[idx] *= 10;
}
// ✅ 安全方式 3:用算法返回的新迭代器
v = {1, 2, 3, 4, 5};
auto new_end = std::remove_if(v.begin(), v.end(), [](int x) { return x < 3; });
v.erase(new_end, v.end()); // 删除 < 3 的元素
return 0;
}
#include <vector>
#include <iostream>
#include <string>
struct Point {
int x, y;
Point(int x_, int y_) : x(x_), y(y_) {
std::cout << "Point constructed: (" << x << ", " << y << ")\n";
}
Point(const Point& p) : x(p.x), y(p.y) {
std::cout << "Point copied\n";
}
};
int main() {
std::vector<Point> points;
std::cout << "=== push_back ===\n";
points.push_back(Point(1, 2)); // 临时对象被构造,然后拷贝/移动到 vector
std::cout << "\n=== emplace_back ===\n";
points.emplace_back(3, 4); // 直接在 vector 末尾构造
// emplace_back 更高效,避免临时对象的拷贝/移动
return 0;
}
#include <vector>
#include <iostream>
#include <string>
int main() {
std::vector<std::string> words = {"apple", "cherry"};
// insert - 插入元素(拷贝或移动)
words.insert(words.begin() + 1, "banana"); // {"apple", "banana", "cherry"}
words.insert(words.end(), "date"); // {"apple", "banana", "cherry", "date"}
// insert - 插入多个相同元素
words.insert(words.begin(), 2, "???"); // {"???", "???", "apple", "banana", "cherry", "date"}
// insert - 从另一个范围插入
std::vector<std::string> more = {"x", "y", "z"};
words.insert(words.end(), more.begin(), more.end()); // 添加更多
// emplace - 原位构造
words.emplace(words.begin() + 2, "embedded"); // 直接构造,不拷贝
// erase - 删除单个元素
words.erase(words.begin() + 1); // 删除第二个
// erase - 删除范围
words.erase(words.begin(), words.begin() + 2); // 删除前两个
// 打印
for (const auto& w : words) std::cout << w << " ";
std::cout << std::endl;
return 0;
}
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 删除所有偶数
auto new_end = std::remove_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});
v.erase(new_end, v.end()); // {1, 3, 5, 7, 9}
// 删除第一个满足条件的
v = {1, 2, 3, 2, 4, 2, 5};
auto it = std::find(v.begin(), v.end(), 2);
if (it != v.end()) {
v.erase(it); // 删除第一个 2
}
// {1, 3, 2, 4, 2, 5}
// 删除所有特定值
v = {1, 2, 3, 2, 4, 2, 5};
v.erase(std::remove(v.begin(), v.end(), 2), v.end()); // {1, 3, 4, 5}
// 按条件删除(Lambda)
v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
v.erase(std::remove_if(v.begin(), v.end(),
[](int x) { return x > 5; }), v.end()); // {1, 2, 3, 4, 5}
for (int x : v) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
#include <vector>
#include <iostream>
int main() {
std::vector<int> v;
std::cout << "Initial state:\n";
std::cout << " size: " << v.size() << "\n";
std::cout << " capacity: " << v.capacity() << "\n";
// 预留空间(避免多次重新分配)
v.reserve(100);
std::cout << "\nAfter reserve(100):\n";
std::cout << " size: " << v.size() << "\n";
std::cout << " capacity: " << v.capacity() << "\n";
// 添加元素
for (int i = 0; i < 50; ++i) {
v.push_back(i);
}
std::cout << "\nAfter 50 push_backs:\n";
std::cout << " size: " << v.size() << "\n";
std::cout << " capacity: " << v.capacity() << "\n";
// 释放多余容量
v.shrink_to_fit();
std::cout << "\nAfter shrink_to_fit():\n";
std::cout << " size: " << v.size() << "\n";
std::cout << " capacity: " << v.capacity() << "\n";
// 请求最小容量
v.reserve(200);
std::cout << "\nAfter reserve(200):\n";
std::cout << " capacity: " << v.capacity() << "\n";
return 0;
}
#include <vector>
#include <iostream>
int main() {
// 典型的容量增长策略
std::vector<int> v;
std::cout << "Capacity growth:\n";
for (int i = 0; i < 20; ++i) {
if (i == 0 || v.capacity() != v.size() - 1) {
std::cout << "Size " << v.size()
<< " -> Capacity " << v.capacity() << "\n";
}
v.push_back(i);
}
// 注:容量增长是实现定义的,通常是 1.5x 或 2x
// GCC: 2x, MSVC: 1.5x
return 0;
}
#include <vector>
#include <iostream>
int main() {
// ❌ 低效:大量小规模 push_back
std::vector<int> inefficient;
for (int i = 0; i < 10000; ++i) {
inefficient.push_back(i); // 可能多次重新分配
}
// ✅ 高效:先 reserve
std::vector<int> efficient;
efficient.reserve(10000); // 预分配
for (int i = 0; i < 10000; ++i) {
efficient.push_back(i); // 不重新分配
}
// ✅ 也可以用 resize + 操作
std::vector<int> v(10000); // 直接分配 10000 个元素
for (int i = 0; i < 10000; ++i) {
v[i] = i; // 使用下标操作
}
return 0;
}
#include <vector>
#include <iostream>
#include <new>
int main() {
// push_back 可能抛出 bad_alloc(内存不足)
try {
std::vector<int> v;
v.reserve(std::numeric_limits<std::size_t>::max()); // 尝试分配过大内存
} catch (const std::bad_alloc& e) {
std::cout << "Memory allocation failed: " << e.what() << "\n";
}
// emplace_back 也可能抛出
std::vector<std::vector<int>> vv;
try {
for (int i = 0; i < 1000000; ++i) {
vv.emplace_back(1000000); // 每次分配大块内存
}
} catch (const std::bad_alloc& e) {
std::cout << "Out of memory!\n";
}
return 0;
}
#include <vector>
#include <iostream>
#include <iomanip>
int main() {
// 3x4 的二维 vector,初始值为 0
std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
// 设置值
matrix[0][0] = 1;
matrix[1][2] = 5;
// 打印
for (const auto& row : matrix) {
for (int val : row) {
std::cout << std::setw(3) << val << " ";
}
std::cout << "\n";
}
// 访问元素
int val = matrix[2][3]; // 第 3 行,第 4 列
return 0;
}
#include <vector>
#include <iostream>
// 创建任意大小的二维 vector
std::vector<std::vector<double>> create_matrix(int rows, int cols, double init = 0.0) {
return std::vector<std::vector<double>>(
rows, std::vector<double>(cols, init));
}
// 更好的方式:用一维 vector 模拟(更高效)
std::vector<int> create_flat_matrix(int rows, int cols, int init = 0) {
return std::vector<int>(rows * cols, init);
}
// 访问扁平矩阵
int get(const std::vector<int>& mat, int rows, int cols, int row, int col) {
return mat[row * cols + col];
}
void set(std::vector<int>& mat, int cols, int row, int col, int value) {
mat[row * cols + col] = value;
}
int main() {
auto matrix = create_matrix(3, 4, 1.0);
// 扁平矩阵更高效(连续内存,缓存友好)
auto flat = create_flat_matrix(3, 4, 0);
set(flat, 4, 1, 2, 99);
std::cout << get(flat, 4, 1, 2) << std::endl; // 99
return 0;
}
#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <functional>
int main() {
std::vector<int> v = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// 排序
std::sort(v.begin(), v.end()); // 升序
std::sort(v.begin(), v.end(), std::greater<int>()); // 降序
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // Lambda
// 查找
auto it = std::find(v.begin(), v.end(), 5);
auto it2 = std::find_if(v.begin(), v.end(), [](int x) { return x > 5; });
// 二分查找(需要已排序)
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), 5);
// 统计
int count = std::count(v.begin(), v.end(), 3);
int evens = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
// 求和
int sum = std::accumulate(v.begin(), v.end(), 0);
double avg = static_cast<double>(sum) / v.size();
// 最大/最小
int max_val = *std::max_element(v.begin(), v.end());
int min_val = *std::min_element(v.begin(), v.end());
// 反序
std::reverse(v.begin(), v.end());
// 去重(需先排序)
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
// 洗牌(随机)
std::random_shuffle(v.begin(), v.end()); // C++14 已弃用
// C++20 用 std::ranges::shuffle
return 0;
}
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 转换(平方)
std::vector<int> squares;
std::transform(numbers.begin(), numbers.end(), std::back_inserter(squares),
[](int x) { return x * x; });
// squares = {1, 4, 9, 16, 25}
// 原地转换
std::transform(numbers.begin(), numbers.end(), numbers.begin(),
[](int x) { return x * 2; });
// numbers = {2, 4, 6, 8, 10}
// 两个向量操作
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {10, 20, 30};
std::vector<int> sums;
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(sums),
[](int x, int y) { return x + y; });
// sums = {11, 22, 33}
// 字符串转换
std::vector<std::string> words = {"hello", "world"};
std::vector<std::string> upper;
std::transform(words.begin(), words.end(), std::back_inserter(upper),
[](const std::string& s) {
std::string result = s;
std::transform(result.begin(), result.end(), result.begin(),
::toupper);
return result;
});
// upper = {"HELLO", "WORLD"}
return 0;
}
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <numeric>
struct Student {
std::string name;
std::vector<double> scores;
double average() const {
if (scores.empty()) return 0.0;
return std::accumulate(scores.begin(), scores.end(), 0.0) / scores.size();
}
double max_score() const {
if (scores.empty()) return 0.0;
return *std::max_element(scores.begin(), scores.end());
}
double min_score() const {
if (scores.empty()) return 0.0;
return *std::min_element(scores.begin(), scores.end());
}
};
class GradeBook {
private:
std::vector<Student> students_;
public:
void add_student(const std::string& name) {
students_.push_back({name, {}});
}
void add_score(const std::string& name, double score) {
auto it = std::find_if(students_.begin(), students_.end(),
[&name](const Student& s) { return s.name == name; });
if (it != students_.end()) {
it->scores.push_back(score);
}
}
void print_report() const {
std::cout << std::string(60, '-') << "\n";
std::cout << std::left << std::setw(15) << "Name"
<< std::right << std::setw(10) << "Average"
<< std::setw(10) << "Max"
<< std::setw(10) << "Min"
<< "\n";
std::cout << std::string(60, '-') << "\n";
for (const auto& student : students_) {
std::cout << std::left << std::setw(15) << student.name
<< std::right << std::fixed << std::setprecision(2)
<< std::setw(10) << student.average()
<< std::setw(10) << student.max_score()
<< std::setw(10) << student.min_score()
<< "\n";
}
// 班级平均
double class_avg = 0;
int count = 0;
for (const auto& s : students_) {
class_avg += s.average() * s.scores.size();
count += s.scores.size();
}
if (count > 0) {
std::cout << std::string(60, '-') << "\n";
std::cout << "Class Average: " << std::fixed << std::setprecision(2)
<< (class_avg / count) << "\n";
}
}
// 获取排名
std::vector<std::string> get_top_students(int n) const {
std::vector<const Student*> sorted;
for (const auto& s : students_) {
sorted.push_back(&s);
}
std::sort(sorted.begin(), sorted.end(),
[](const Student* a, const Student* b) {
return a->average() > b->average();
});
std::vector<std::string> result;
for (int i = 0; i < n && i < sorted.size(); ++i) {
result.push_back(sorted[i]->name);
}
return result;
}
};
int main() {
GradeBook book;
book.add_student("Alice");
book.add_student("Bob");
book.add_student("Charlie");
book.add_student("Diana");
// 添加成绩
book.add_score("Alice", 95);
book.add_score("Alice", 88);
book.add_score("Alice", 92);
book.add_score("Bob", 78);
book.add_score("Bob", 85);
book.add_score("Bob", 82);
book.add_score("Charlie", 91);
book.add_score("Charlie", 94);
book.add_score("Charlie", 89);
book.add_score("Diana", 83);
book.add_score("Diana", 90);
book.add_score("Diana", 88);
book.print_report();
std::cout << "\nTop 2 students:\n";
for (const auto& name : book.get_top_students(2)) {
std::cout << "- " << name << "\n";
}
return 0;
}
操作说明
vector<T> v默认构造,空 vector
vector<T> v(n)n 个值初始化元素
vector<T> v(n, val)n 个 val
vector<T> v = {a, b, c}列表初始化
v.push_back(x)添加到末尾(拷贝/移动)
v.emplace_back(args)原位构造(更高效)
v.insert(it, x)在迭代器前插入
v.erase(it)删除迭代器处元素
v.reserve(n)预分配 n 个元素空间
v.shrink_to_fit()释放多余容量
v.size()元素个数
v.capacity()已分配空间
v[0] / v.at(0)访问元素
v.front() / v.back()首/尾元素
v.begin() / v.end()迭代器

性能提示:

  • 大量插入前先 reserve()
  • 优先用 emplace 而不是 push
  • 修改后注意迭代器可能失效

下章预告:ch15 学习 std::unordered_map,哈希表的 C++ 实现。