C++ STL vector 原理到模拟实现

1. vector 核心认识

  • vector 是可变长数组,底层连续存储,支持随机访问。
  • 维护三个指针:
    • _start:数据起始
    • _finish:有效数据末尾
    • _end_of_storage:容量末尾
  • 扩容机制:VS (PJ) 约 1.5 倍 ,G++(SGI) 2 倍
  • 关键特性:支持随机访问、尾部增删 O (1)、中间增删 O (N)、迭代器可能失效

2. 必须掌握的核心接口

2.1 构造函数

cpp 复制代码
// 1. 无参构造
vector();
// 2. 构造 n 个 val
vector(size_t n, const T& val = T());
// 3. 拷贝构造(深拷贝)
vector(const vector& v);
// 4. 迭代器区间构造
template<class InputIterator>
vector(InputIterator first, InputIterator last);

2.2 容量与大小

cpp 复制代码
size_t size() const;         // 有效数据个数
size_t capacity() const;     // 容量
bool empty() const;          // 是否为空
void reserve(size_t n);      // 只改容量,不初始化
void resize(size_t n, const T& val = T()); // 改size+初始化

2.3 增删改查

cpp 复制代码
void push_back(const T& x);     // 尾插
void pop_back();                // 尾删
iterator insert(iterator pos, const T& x); // 插入
iterator erase(iterator pos);           // 删除
T& operator[](size_t i);                // 随机访问
void swap(vector<T>& v);                // 交换(O(1))

3. 迭代器失效

3.1 导致失效的操作

所有引起扩容 的操作:reserve/resize/insert/push_back底层空间被释放,原迭代器变成野指针。

3.2 erase 导致失效

删除后元素前移,被删位置迭代器失效。正确写法

cpp 复制代码
it = v.erase(it); 

而非直接 erase(it)++it

3.3 解决方法

使用前重新获取迭代器


4. 深拷贝与 memcpy 陷阱

  • vector 扩容必须深拷贝,逐个元素赋值。
  • 禁止用 memcpy :memcpy 是浅拷贝,若元素是 string/vector 等带资源类型,会导致重复释放、崩溃

5. vector 模拟实现(可直接运行)

5.1 成员变量

cpp 复制代码
template<class T>
class vector
{
public:
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _end_of_storage = nullptr;
};

5.2 构造 / 析构 / 拷贝 / 赋值

cpp 复制代码
// 无参构造
vector() = default;

// n 个 val
vector(size_t n, const T& val = T())
{
    reserve(n);
    for (size_t i = 0; i < n; ++i)
        _start[i] = val;
    _finish = _start + n;
}

// 迭代器区间构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
    while (first != last)
        push_back(*first++);
}

// 拷贝构造(深拷贝)
vector(const vector<T>& v)
{
    reserve(v.capacity());
    for (size_t i = 0; i < v.size(); ++i)
        _start[i] = v._start[i];
    _finish = _start + v.size();
    _end_of_storage = _start + v.capacity();
}

// 赋值重载(现代写法)
vector<T>& operator=(vector<T> v)
{
    swap(v);
    return *this;
}

// 析构
~vector()
{
    delete[] _start;
    _start = _finish = _end_of_storage = nullptr;
}

5.3 迭代器

cpp 复制代码
iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }

5.4 容量 / 大小 / 访问

cpp 复制代码
size_t size() const { return _finish - _start; }
size_t capacity() const { return _end_of_storage - _start; }
bool empty() const { return size() == 0; }

T& operator[](size_t i) { return _start[i]; }
const T& operator[](size_t i) const { return _start[i]; }

5.5 reserve / resize

cpp 复制代码
void reserve(size_t n)
{
    if (n > capacity())
    {
        size_t oldSize = size();
        T* tmp = new T[n];
        for (size_t i = 0; i < oldSize; ++i)
            tmp[i] = _start[i];
        delete[] _start;
        _start = tmp;
        _finish = _start + oldSize;
        _end_of_storage = _start + n;
    }
}

void resize(size_t n, const T& val = T())
{
    size_t oldSize = size();
    if (n > oldSize)
    {
        reserve(n);
        for (size_t i = oldSize; i < n; ++i)
            _start[i] = val;
    }
    _finish = _start + n;
}

5.6 增删

cpp 复制代码
void push_back(const T& x)
{
    if (_finish == _end_of_storage)
        reserve(capacity() == 0 ? 4 : capacity() * 2);
    *_finish++ = x;
}

void pop_back()
{
    assert(!empty());
    --_finish;
}

iterator insert(iterator pos, const T& x)
{
    size_t len = pos - _start;
    if (_finish == _end_of_storage)
        reserve(capacity() == 0 ? 4 : capacity() * 2);
    pos = _start + len;
    for (iterator it = _finish; it > pos; --it)
        *it = *(it - 1);
    *pos = x;
    ++_finish;
    return pos;
}

iterator erase(iterator pos)
{
    for (iterator it = pos; it < end() - 1; ++it)
        *it = *(it + 1);
    --_finish;
    return pos;
}

void swap(vector<T>& v)
{
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_end_of_storage, v._end_of_storage);
}

6. 总结

  1. vector 是连续存储的动态数组。
  2. 扩容是开新空间→拷贝→释放旧空间,不是原地扩展。
  3. 迭代器失效是手写 vector 最常见 bug。
  4. 元素带资源时不能用 memcpy,必须深拷贝。
  5. reserve 只扩容量,resize 改 size 并初始化。
  6. 现代写法赋值运算符:传参拷贝 + swap,异常安全、极简。
相关推荐
bIo7lyA8v22 分钟前
算法复杂度评估的实验统计方法与可视化的技术8
算法
李老师讲编程43 分钟前
中国电子学会图形化2020.12月Scratch三级考级题
算法·scratch·信息学奥赛·图形化编程·scratch素材
ao-weilai1 小时前
C++:哈希表
c++·哈希算法·散列表
汉克老师1 小时前
GESP7级C++考试语法知识(二、指数函数(1、pow() 函数)
c++·指数函数·pow·gesp7级·精度误差
退休倒计时1 小时前
【每日一题】LeetCode 53. 最大子数组和 TypeScript
数据结构·算法·leetcode·typescript
旖-旎1 小时前
FloodFill(图像渲染)(1)
c++·算法·深度优先·力扣
戴西软件1 小时前
戴西 DLM 许可授权管理系统:破解无网络环境下工业软件授权难题,助力制造企业降本增效
网络·人工智能·python·深度学习·程序人生·算法·制造
2601_961875241 小时前
法考资料2026|全套|资料已整理
数据结构·算法·链表·贪心算法·eclipse·线性回归·动态规划
无限码力2 小时前
美团研发岗 4月18号笔试真题 - 坐标
算法·美团笔试真题·美团笔试题·美团研发岗笔试题·美团研发岗4月18号真题