C++ STL std::vector 底层实现

📌 C++ STL std::vector 底层实现

std::vectorC++ STL 中最常用的 动态数组容器,其底层实现依赖于 连续内存块,并采用 动态扩容策略 来管理内存。


📌 1. std::vector 的底层数据结构

🚀 std::vector 内部维护三个指针

template <typename T>
class vector {
private:
    T* _start;     // 指向数据存储的起始位置
    T* _finish;    // 指向数据存储的末尾(size)
    T* _end_of_storage; // 指向已分配空间的末尾(capacity)
};

📌 关键点

  • _start:指向数组起始地址,即 begin()
  • _finish:指向当前已存储数据的末尾size()
  • _end_of_storage:指向分配内存的末尾capacity()

📌 示意图

_capacity = 5, _size = 3
 ┌─────────┬─────────┬─────────┬─────────┬─────────┐
 │   10    │   20    │   30    │   -     │   -     │
 ├─────────┴─────────┴─────────┬─────────┬─────────┤
 ↑_start                      ↑_finish   ↑_end_of_storage

这样 std::vector 支持 O(1) 访问,并允许动态扩容!


📌 2. std::vector 的关键操作

🚀 2.1 插入元素 (push_back())

📌 流程

  1. 检查 size() 是否小于 capacity()
    • 如果足够,直接 *(finish) = value,然后 _finish++
    • 如果容量不够,触发 扩容(reallocate)

📌 代码

void push_back(const T& value) {
    if (_finish == _end_of_storage) {
        reallocate(); // 扩容
    }
    *_finish = value;  // 直接赋值
    _finish++;
}

push_back() 均摊 O(1),但扩容时 O(N)


🚀 2.2 vector 扩容机制(reallocate()

📌 size() == capacity() 时,vector 需要扩容

  • 默认扩容 2 倍,减少扩容次数,提高性能!
  • 分配更大的内存块,然后 移动旧数据到新内存

📌 扩容代码

void reallocate() {
    size_t new_capacity = capacity() ? 2 * capacity() : 1;
    T* new_start = new T[new_capacity]; // 分配新内存
    std::move(_start, _finish, new_start); // 移动数据
    delete[] _start; // 释放旧内存
    _finish = new_start + size();
    _start = new_start;
    _end_of_storage = _start + new_capacity;
}

reallocate() 触发 O(N) 级别的拷贝,但均摊成本仍为 O(1)

📌 扩容过程

初始容量 4:
 ┌─────┬─────┬─────┬─────┐
 │  10 │  20 │  30 │  40 │
 └─────┴─────┴─────┴─────┘
 ↑_start             ↑_finish

触发扩容(2 倍):
 ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
 │  10 │  20 │  30 │  40 │     │     │     │     │
 └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
 ↑_start                                        ↑_end_of_storage

这样 vector 避免了频繁 malloc(),提高插入效率!


🚀 2.3 删除元素 (pop_back())

📌 流程

  • 直接 _finish--,然后析构最后一个元素。

📌 代码

void pop_back() {
    if (_finish != _start) {
        --_finish;
        _finish->~T(); // 手动调用析构函数
    }
}

pop_back() 始终是 O(1)


🚀 2.4 erase() 删除任意位置元素

📌 流程

  • 需要 移动后续元素覆盖删除的元素,因此时间复杂度 O(N)

📌 代码

iterator erase(iterator pos) {
    if (pos + 1 != _finish) {
        std::move(pos + 1, _finish, pos); // 移动数据覆盖删除位置
    }
    --_finish;
    return pos;
}

erase() 需要移动数据,因此时间复杂度 O(N)


🚀 2.5 reserve() 预分配容量

📌 作用

  • reserve(n) 预先分配 n 个元素的空间,避免频繁扩容。

📌 代码

void reserve(size_t new_capacity) {
    if (new_capacity > capacity()) {
        T* new_start = new T[new_capacity];
        std::move(_start, _finish, new_start);
        delete[] _start;
        _start = new_start;
        _finish = new_start + size();
        _end_of_storage = _start + new_capacity;
    }
}

适用于大量 push_back(),减少扩容开销!


📌 3. vector 的迭代器失效

std::vector 的操作可能导致迭代器失效

操作是否导致迭代器失效原因
push_back()可能触发扩容,导致指针失效
pop_back()最后一个元素被删除,指针失效
erase(it)后续元素前移,旧迭代器无效
clear()删除所有元素,所有迭代器失效

最佳实践

vec.push_back(10);
it = vec.begin();  // 重新获取迭代器

erase() 后应使用返回值

it = vec.erase(it); // 获取新的有效迭代器

📌 4. vector vs list vs deque

容器底层结构插入效率访问效率迭代器稳定性
vector连续数组慢(O(N))快(O(1))可能失效
list双向链表快(O(1))慢(O(N))稳定
deque分段数组快(O(1))快(O(1))较稳定

vector 适用于 频繁 随机访问O(1))。
list 适用于 频繁 插入/删除O(1))。
deque 适用于 频繁 头部/尾部插入O(1))。


📌 5. 总结

vector 采用动态数组存储,支持 O(1) 访问
扩容时 2x 方式 reallocate(),减少 malloc() 次数
push_back() 均摊 O(1)erase() O(N),迭代器可能失效
reserve(n) 预分配可优化 push_back()

🚀 掌握 vector 底层原理,写出更高效的 C++ 代码! 🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值