std::vector是c++标准库提供的动态数组,自动管理堆内存、扩容、析构与异常安全;误用new[]/delete[]易致泄漏、越界、违反raii;应优先用push_back()/emplace_back()、reserve()预分配、resize()调整逻辑大小,注意自定义类型需正确实现移动/拷贝语义。

std::vector 是最直接的动态数组实现
不用自己造轮子,std::vector 就是 C++ 标准库提供的动态数组。它在堆上管理内存,自动处理扩容、析构和异常安全,绝大多数场景下就是正确答案。
常见误用是手动 new[] + delete[] 模拟扩容:写起来麻烦,容易漏掉拷贝构造、异常时内存泄漏、不支持移动语义,还绕过 RAII。
实操建议:
- 声明即用:
std::vector<int> arr;</int>,初始为空;std::vector<:string> names(5);</:string>预分配 5 个默认构造对象 - 添加元素优先用
push_back()(尾插)或emplace_back()(就地构造),避免反复insert()中间位置 - 扩容策略由实现决定(通常是 1.5× 或 2×),可通过
reserve()预留空间减少重分配次数 - 注意
size()是当前元素个数,capacity()是已分配但未使用的空间大小
手动实现扩容逻辑时必须重拷贝/移动已有元素
如果出于学习或特殊约束必须手写,核心不是“怎么申请新内存”,而是“怎么把旧数据安全迁移到新内存”。漏掉这步,数据就丢了。
典型错误现象:arr[0] 访问越界、值为随机垃圾、程序崩溃——往往是因为只 new 了新内存,却没把旧数组内容复制过去。
关键点:
- 旧数组指针非空才需要拷贝;若使用
std::move,对可移动类型应优先调用移动构造而非拷贝 - 必须按对象生命周期顺序调用旧对象的析构函数(尤其含资源管理类),再释放原始内存
- 不能直接
memcpy非 POD 类型(如含虚函数、引用、std::string 的类),会跳过构造/析构逻辑 - 异常安全:若拷贝过程抛异常,要保证原数组不被破坏(通常先分配新内存,成功后再清理旧内存)
resize() 和 reserve() 的区别常被混淆
resize(n) 改变的是逻辑大小:元素数量变成 n,多出的用默认值填充,少掉的被析构;reserve(n) 只影响物理容量:确保至少能存下 n 个元素,但 size() 不变,也不构造任何对象。
性能影响明显:频繁 push_back() 而不 reserve(),可能触发多次内存重分配+全量拷贝;但过度 reserve() 浪费内存。
使用场景举例:
- 已知后续要插入 1000 个元素 → 先
v.reserve(1000),再循环push_back() - 需要一个长度为 50 的数组,每个元素初始化为 -1 →
v.resize(50, -1) - 清空并回收内存 →
v.clear(); std::vector<int>().swap(v);</int>(C++11 后更推荐v.shrink_to_fit(),但不保证一定释放)
自定义类型需检查移动/拷贝语义是否正确
当 std::vector 存储自定义类时,扩容过程会调用其拷贝构造函数或移动构造函数。如果类里有裸指针、文件句柄或引用成员,而你没显式定义这些函数,编译器生成的默认版本很可能出错。
典型错误现象:扩容后两个元素指向同一块内存,修改一个导致另一个失效;或析构时 double-free。
验证方法:
- 加日志到拷贝/移动构造函数和析构函数,观察
push_back()和resize()时的调用次数与顺序 - 用
valgrind或 ASan 检测内存非法访问和泄漏 - 确保类满足“可移动构造”(
noexcept移动构造更优)和“可拷贝构造”,否则 vector 扩容可能退化为仅支持拷贝(影响性能)
真正难的不是“怎么让数组变大”,而是“怎么让变大的过程不破坏已有状态、不泄露资源、不违反对象语义”。哪怕用 std::vector,只要里面塞了管理资源的类,就得盯紧它的移动和析构行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











