vector动态数组
动态数组就是由用户申请的一块连续的动态内存封装了相关的操作接口之后就成了vector。1、vector 动态数组 和 静态数组的区别维度静态数组C-style / std::array动态数组std::vector内存位置栈或全局区堆大小编译期固定运行期动态增长扩容不支持自动扩容2倍或1.5倍底层连续内存块连续内存块 三个指针核心共性两者都是连续内存支持 O(1) 随机访问。2、 vector 的三指针模型┌─────────────────────────────────────────┐ │ _M_start → 指向首元素 │ │ _M_finish → 指向最后一个元素的下一个位置 │ │ _M_end_of_storage → 指向分配内存的末尾 │ └─────────────────────────────────────────┘ [a][b][c][d][ ][ ][ ][ ] ↑ ↑ ↑ start finish end_of_storage size4 capacity83、vector的扩容机制当调用push_back 函数插入元素vector内存空间不足时引发扩容机制。1申请新的内存空间一般是原内存空间的2倍VS是1.5倍。2将原内存空间内的数据拷贝到新的内存空间。3释放旧内存空间。// 当 size capacity再 push_back 时 void push_back(const T val) { if (finish ! end_of_storage) { // 有空间直接构造 construct(finish, val); finish; } else { // 没空间 → 扩容 size_t new_capacity (capacity() 0) ? 1 : capacity() * 2; // GCC: 2倍; VS: 1.5倍 T* new_start allocate(new_capacity); // 把旧元素搬过去浅拷贝 or 移动构造 for (size_t i 0; i size(); i) { new_start[i] std::move(old_start[i]); } deallocate(old_start); // 释放旧内存 // 更新三个指针 } }扩容时所有指向元素的迭代器、指针和引用确实会全部失效。扩容时vector会申请一块更大的新内存将原有元素拷贝或移动到新内存中然后释放旧内存。任何指向旧内存的“地址”都成了悬垂指针继续使用将导致未定义行为。即使没有触发扩容push_back等操作也会使end()迭代器失效。例如std::vectorint v; v.reserve(10); // 预留空间capacity 10 auto it v.end(); // 指向末尾此时末尾是begin v.push_back(1); // 未扩容size 从 0 变 1 // it 仍然指向“末尾”但现在末尾已经变成元素1之后了 // 实际上 it 已失效不能再用它去访问元素14、vector 的 push_back 和 emplace_back 区别v.push_back(MyClass(1, 2)); // 构造临时对象 → 拷贝/移动进 vector v.emplace_back(1, 2); // 直接在 vector 内存里构造零拷贝push_back它会先创建一个临时 MyClass 对象在栈上或寄存器中。 然后将这个临时对象移动或拷贝到 vector 中默认会先进行移动对象不支持移动构造时触发拷贝如果连拷贝构造也不支持那么就会编译失败。5、vector 扩容后迭代器为什么失效扩容会重新分配内存并把旧内存的元素搬过去旧内存被释放。原来指向旧地址的迭代器/指针就变成了悬空指针访问会产生未定义行为报错。vectorint v {1, 2, 3}; auto it v.begin(); v.push_back(4); // 可能扩容 // it 此时已失效不能再使用6、reserve 和 resize 的区别reserve(n)resize(n)改变 size❌ 不改变✅ 改变改变 capacity✅ 可能增大✅ 可能增大构造元素❌ 不构造✅ 构造 n 个元素用途预分配避免扩容改变实际元素个数vectorint v; v.reserve(100); // capacity100, size0, 没有元素 v.resize(100); // capacity100, size100, 100个07、vector 底层是连续内存怎么证明// 1. v[i1] - v[i] 1地址连续 // 2. vector 可以兼容 C 接口v[0] 或 v.data() void c_func(int* arr, size_t n); c_func(v.data(), v.size()); // 可以直接传8、为什么 vector 的 operator[] 不检查越界at() 会v[10]; // 不检查越界 → 未定义行为但快 v.at(10); // 抛 std::out_of_range 异常安全[ ] 追求零开销抽象和 C 数组一致at() 提供安全版本。面试时可以补充自己写库代码用 at()写性能敏感代码用 [ ]。9、vector 内存什么时候释放clear() 会释放吗clear() 只析构元素不改变 capacity内存不释放shrink_to_fit() 请求缩减到 size但标准说请求执行可能会不成功执行原理是重新申请一块新内存然后将旧数据拷贝过去再真正释放vector 析构时或 vectorT().swap(v) 这个经典技巧