在C++开发中,动态数组是指容量可以随着元素数量变化自动调整的数组结构,vector是标准库提供的动态数组实现,其动态扩容机制是保证灵活使用的关键。vector通过内部管理堆内存的方式,在元素数量超过当前容量时自动申请更大的内存空间,完成元素迁移后释放旧空间,整个过程对使用者透明。

vector动态扩容的核心原理
底层存储结构
vector内部通常维护三个指针:指向首元素的_M_start、指向最后一个元素下一个位置的_M_finish、指向当前分配内存末尾的_M_end_of_storage。其中_M_finish - _M_start得到当前元素数量,_M_end_of_storage - _M_start得到当前总容量。当元素数量等于容量时,再次插入元素就会触发扩容。
扩容触发与流程
vector的扩容不是每次插入都执行,只有当size() == capacity()时才会触发。扩容的基本流程如下:
- 计算新的容量,通常是原容量的1.5倍或者2倍,不同编译器实现略有差异
- 申请一块大小为new_capacity * sizeof(T)的堆内存
- 将旧内存中的所有元素拷贝或移动到新内存中
- 释放旧的内存空间
- 更新内部指针,指向新的内存区域
代码示例验证扩容行为
以下代码可以观察vector的容量变化:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 初始容量通常为0
std::cout << "初始容量: " << vec.capacity() << std::endl;
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
std::cout << "插入元素" << i << "后,容量: " << vec.capacity() << std::endl;
}
return 0;
}
运行上述代码可以看到,当元素数量超过当前容量时,容量会成倍增长,比如从1到2,2到4,4到8,8到16这样的变化规律。
手动实现简易动态数组
为了更直观理解动态数组的实现,我们可以手动实现一个简化版的动态数组:
#include <iostream>
#include <cstring>
template <typename T>
class SimpleDynamicArray {
private:
T* data; // 指向堆内存的指针
size_t cur_size; // 当前元素数量
size_t cur_capacity; // 当前总容量
// 扩容函数,新容量为原容量的2倍
void expand() {
size_t new_capacity = cur_capacity == 0 ? 1 : cur_capacity * 2;
T* new_data = new T[new_capacity];
// 拷贝旧元素到新内存
for (size_t i = 0; i < cur_size; ++i) {
new_data[i] = data[i];
}
// 释放旧内存
delete[] data;
data = new_data;
cur_capacity = new_capacity;
}
public:
SimpleDynamicArray() : data(nullptr), cur_size(0), cur_capacity(0) {}
~SimpleDynamicArray() {
delete[] data;
}
// 添加元素
void push_back(const T& val) {
if (cur_size == cur_capacity) {
expand();
}
data[cur_size++] = val;
}
// 获取元素
T& operator[](size_t index) {
return data[index];
}
// 获取当前元素数量
size_t size() const {
return cur_size;
}
// 获取当前容量
size_t capacity() const {
return cur_capacity;
}
};
int main() {
SimpleDynamicArray<int> arr;
for (int i = 0; i < 5; ++i) {
arr.push_back(i);
std::cout << "插入" << i << "后,size: " << arr.size() << ", capacity: " << arr.capacity() << std::endl;
}
return 0;
}
vector扩容的注意事项
vector扩容会导致原有迭代器、指针、引用全部失效,因为元素的内存地址已经发生了变化。如果在循环中需要插入元素,最好提前预留足够的容量,减少扩容次数。可以使用reserve()方法预先分配容量:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 预先分配10个元素的容量
vec.reserve(10);
std::cout << "预分配后容量: " << vec.capacity() << std::endl;
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
// 此时不会触发扩容
}
std::cout << "插入10个元素后容量: " << vec.capacity() << std::endl;
return 0;
}
另外,如果元素类型没有拷贝或移动构造函数,vector的扩容过程会失败,因此自定义类型需要保证具备正确的拷贝或移动语义,才能正常使用vector存储。