内存管理
std::vector
:
std::vector
是动态数组,其内存管理由容器自动处理。它会在需要时自动分配和释放内存,当元素数量增加超过当前容量时,会重新分配更大的内存块,并将原数据复制过去。例如:
#include <vector>
std::vector<int> vec;
vec.push_back(1); // 此时vec可能分配一个较小容量的内存块
for (int i = 0; i < 1000; ++i) {
vec.push_back(i); // 随着元素增加,vec可能多次重新分配内存
}
- 析构时,`std::vector` 会自动释放其占用的所有内存。
- 普通数组:
- 普通数组的内存分配是静态的(在栈上分配)或由程序员手动使用
new[]
(在堆上分配)。例如:
int arr1[10]; // 栈上分配固定大小为10的数组
int* arr2 = new int[10]; // 堆上分配大小为10的数组
- 使用 `new[]` 分配的数组需要手动使用 `delete[]` 释放内存,否则会导致内存泄漏。例如:
delete[] arr2;
访问效率
std::vector
:
std::vector
支持随机访问,通过下标操作符 []
或 at()
成员函数访问元素,其时间复杂度为 $O(1)$。因为 std::vector
在内存中是连续存储的,与普通数组类似,CPU 的缓存机制可以有效提高访问效率。例如:
std::vector<int> vec = {1, 2, 3, 4, 5};
int value = vec[2]; // 高效访问
- 普通数组:
- 普通数组同样支持随机访问,通过下标操作符
[]
访问元素,时间复杂度也是 $O(1)$。由于其内存连续性,访问效率与 std::vector
相当。例如:
int arr[] = {1, 2, 3, 4, 5};
int value = arr[2]; // 高效访问
插入删除操作
std::vector
:
- 在
std::vector
的末尾插入(push_back
)或删除(pop_back
)元素的时间复杂度为 $O(1)$(不考虑内存重新分配)。但如果在中间或开头插入删除元素,需要移动大量元素,时间复杂度为 $O(n)$。例如:
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.insert(vec.begin() + 2, 10); // 在索引2处插入10,后面元素需依次后移
- 普通数组:
- 普通数组本身没有内置的插入删除操作。如果要在数组中间插入或删除元素,需要手动移动元素,时间复杂度为 $O(n)$。例如,在数组
arr
的索引 index
处插入元素 element
:
int arr[] = {1, 2, 3, 4, 5};
int newArr[6];
int index = 2;
int element = 10;
for (int i = 0; i < index; ++i) {
newArr[i] = arr[i];
}
newArr[index] = element;
for (int i = index; i < 5; ++i) {
newArr[i + 1] = arr[i];
}
实际场景分析
- 使用
std::vector
更优的场景:
- 数据量动态变化:例如在实现一个日志系统,日志条数不确定,随着程序运行不断增加。使用
std::vector
可以方便地动态添加日志记录,无需手动管理内存。
- 需要频繁在末尾插入删除元素:如实现一个简单的栈结构,
std::vector
的 push_back
和 pop_back
操作非常适合这种场景。
- 使用普通数组更合适的场景:
- 数据量固定且已知:例如存储一个图像的像素数据,图像分辨率固定,使用普通数组可以在栈上分配内存,效率较高且无需担心动态内存分配的开销。
- 对内存布局有严格要求:在一些底层开发,如嵌入式系统中,对内存地址和布局有严格要求,普通数组可以更精确地控制内存。