功能描述
本示例程序展示了 C++ 中数组(Array)和向量(Vector)的使用方法,包括:
- 基本数组的定义、初始化和访问
- 多维数组的使用
- std::vector 的基本操作
- 向量的增删改查操作
- 向量的排序和其他算法操作
- 数组和向量的比较
- 动态数组的使用
- 向量的高级特性
代码解析
1. 基本数组操作
1 | int arr[5] = {10, 20, 30, 40, 50}; |
说明:
- 数组是一种固定大小的同类型元素集合。
- 数组的大小在定义时确定,不能动态改变。
- 数组元素通过索引访问,索引从 0 开始。
- 数组的大小可以通过
sizeof(arr) / sizeof(arr[0])计算得到。
2. 多维数组
1 | int matrix[3][3] = { |
说明:
- 多维数组是数组的数组,可以看作是表格或矩阵。
- 二维数组是最常见的多维数组形式。
- 多维数组通过多个索引访问元素。
3. 基本向量操作
1 | std::vector<int> numbers = {1, 2, 3, 4, 5}; |
说明:
std::vector是 C++ 标准库提供的动态数组,大小可以动态改变。- 向量在内存中是连续存储的,支持随机访问。
size()方法返回向量中元素的数量。capacity()方法返回向量当前分配的存储空间大小。
4. 向量的增删改查
1 | numbers.push_back(6); |
说明:
push_back()方法在向量末尾添加元素。pop_back()方法删除向量末尾的元素。insert()方法在指定位置插入元素。erase()方法删除指定位置的元素。front()方法返回向量的第一个元素。back()方法返回向量的最后一个元素。
5. 向量的清空和容量管理
1 | numbers.clear(); |
说明:
clear()方法清空向量中的所有元素,大小变为 0,但容量不变。reserve()方法预分配指定大小的存储空间,避免频繁的内存重新分配。
6. 向量的排序
1 | std::vector<int> sortedNumbers = {5, 2, 8, 1, 9, 3}; |
说明:
std::sort()函数可以对向量进行排序,需要包含<algorithm>头文件。begin()和end()方法返回指向向量开始和结束位置的迭代器。
7. 动态数组(new/delete)
1 | int* dynamicArray = new int[5]; |
说明:
- 使用
new运算符可以创建动态数组,大小在运行时确定。 - 动态数组使用完后需要使用
delete[]运算符释放内存,避免内存泄漏。
8. 向量的高级特性
1 | // 向量的移动语义 |
说明:
- 向量支持移动语义,可以高效地转移资源。
- 向量支持从其他容器的范围构造。
- 向量支持填充构造,用指定值初始化多个元素。
编译和运行
在 Windows 上编译(使用 g++):
1 | g++ -std=c++11 -fexec-charset=GBK -o arrays_vectors arrays_vectors.cpp |
运行程序:
1 | .\arrays_vectors.exe |
输出结果:
1 | === 1. 基本数组操作 === |
技术要点
1. 数组的特性
- 固定大小:数组的大小在定义时确定,不能动态改变。
- 连续存储:数组元素在内存中连续存储,支持随机访问。
- 自动存储:局部数组在栈上分配内存,函数返回后自动释放。
- 静态初始化:数组可以在定义时初始化,也可以稍后逐个赋值。
- 多维数组:支持二维、三维等多维数组。
2. 向量的特性
- 动态大小:向量的大小可以在运行时动态改变。
- 连续存储:向量元素在内存中连续存储,支持随机访问。
- 自动内存管理:向量会自动管理内存的分配和释放。
- 灵活的操作:提供了丰富的成员函数,如
push_back()、pop_back()、insert()、erase()等。 - 迭代器支持:支持使用迭代器进行遍历和操作。
- 算法兼容:可以与 STL 算法无缝配合使用。
3. 数组和向量的比较
| 特性 | 数组 (Array) | 向量 (Vector) |
|---|---|---|
| 大小 | 固定,编译时确定 | 动态,运行时可改变 |
| 内存管理 | 手动(动态数组)或自动(局部数组) | 自动 |
| 访问速度 | 非常快 | 几乎与数组相同 |
| 插入/删除 | 效率低,需要手动移动元素 | 尾部操作高效,中间操作需要移动元素 |
| 功能 | 基本功能,需要手动实现 | 丰富的成员函数和操作 |
| 安全性 | 无边界检查,容易越界 | 提供 at() 方法进行边界检查 |
| 与 STL 算法 | 兼容,但需要手动管理 | 完全兼容,使用方便 |
4. 动态数组与向量
- 动态数组:使用
new和delete[]管理内存,大小在运行时确定,但一旦分配就不能改变。 - 向量:大小在运行时可以动态改变,自动管理内存,提供了更丰富的操作。
5. 向量的容量管理
- size():返回向量中元素的数量。
- capacity():返回向量当前分配的存储空间大小。
- reserve(n):预分配至少能容纳 n 个元素的存储空间。
- resize(n):调整向量大小为 n,新增元素会被默认初始化。
- shrink_to_fit():减少容量以适应大小,释放未使用的内存。
6. 向量的迭代器
- begin():返回指向第一个元素的迭代器。
- end():返回指向最后一个元素之后位置的迭代器。
- rbegin():返回指向最后一个元素的反向迭代器。
- rend():返回指向第一个元素之前位置的反向迭代器。
7. 常用的向量算法
- std::sort():对向量元素进行排序。
- std::reverse():反转向量元素的顺序。
- std::find():查找指定元素。
- std::min_element():查找最小元素。
- std::max_element():查找最大元素。
- std::accumulate():计算元素的累加和。
常见问题
1. 数组越界
问题:访问数组时使用了超出数组范围的索引。
解决方案:确保索引在有效范围内,或使用向量的 at() 方法进行边界检查。
2. 内存泄漏
问题:使用 new 创建动态数组后未使用 delete[] 释放内存。
解决方案:总是在使用完动态数组后调用 delete[] 释放内存,或使用智能指针管理内存。
3. 向量的性能问题
问题:频繁的 push_back() 操作导致频繁的内存重新分配。
解决方案:使用 reserve() 方法预分配足够的存储空间,减少内存重新分配的次数。
4. 向量的清空问题
问题:使用 clear() 后向量的容量不变,仍然占用大量内存。
解决方案:在 clear() 后调用 shrink_to_fit() 释放未使用的内存。
5. 数组到向量的转换
问题:需要将数组转换为向量。
解决方案:使用向量的范围构造函数,如 std::vector<int> vec(arr, arr + size)。
6. 向量的复制问题
问题:复制大向量时效率低下。
解决方案:使用移动语义(std::move())或引用传递,避免不必要的复制。
7. 多维向量的创建
问题:不知道如何创建和初始化多维向量。
解决方案:使用嵌套的向量,如 std::vector<std::vector<int>> matrix(3, std::vector<int>(3))。
8. 向量的比较操作
问题:需要比较两个向量是否相等。
解决方案:使用 == 运算符直接比较两个向量,或使用 std::equal() 算法。
9. 向量的排序稳定性
问题:排序后相等元素的相对顺序可能改变。
解决方案:使用 std::stable_sort() 进行稳定排序。
10. 向量的自定义排序
问题:需要按照自定义规则对向量进行排序。
解决方案:提供自定义的比较函数或使用 lambda 表达式。
代码优化建议
优先使用向量:在大多数情况下,优先使用
std::vector而不是数组,特别是当大小需要动态改变时。合理使用 reserve():对于已知或可预测大小的向量,使用
reserve()预分配存储空间,减少内存重新分配的次数。避免不必要的复制:使用移动语义(
std::move())或引用传递,避免不必要的向量复制。使用迭代器或范围 for 循环:使用迭代器或范围 for 循环遍历向量,代码更简洁、更安全。
利用 STL 算法:使用 STL 算法处理向量,如
std::sort()、std::find()等,代码更简洁、更高效。注意内存管理:对于动态数组,确保使用
delete[]释放内存,避免内存泄漏。使用 at() 进行边界检查:在调试阶段,使用
at()方法访问向量元素,进行边界检查,提高代码安全性。合理使用 shrink_to_fit():在向量大小大幅减小后,使用
shrink_to_fit()释放未使用的内存,减少内存占用。考虑使用其他容器:根据具体需求,考虑使用其他 STL 容器,如
std::list(频繁插入删除)、std::deque(两端操作)等。性能测试:对于性能关键的代码,进行性能测试,比较不同数据结构和算法的性能。
总结
C++ 提供了两种主要的序列容器:
- 数组:固定大小,连续存储,访问速度快,适用于大小已知且不变的场景。
- 向量:动态大小,连续存储,自动内存管理,提供丰富的操作,适用于大小可能改变的场景。
向量是 C++ 中最常用的序列容器,它结合了数组的高效访问和链表的动态大小特性,是大多数情况下的首选数据结构。
通过合理使用数组和向量,可以编写出:
- 高效:利用连续存储的优势,提供快速的随机访问。
- 灵活:向量的动态大小适应不同的需求。
- 安全:向量的自动内存管理和边界检查减少错误。
- 可维护:简洁的代码和丰富的操作提高代码可读性。
掌握数组和向量的使用方法,对于编写高质量的 C++ 代码至关重要。
arrays_vectors.cpp
1 |
|