跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ STL vector 底层原理与模拟实现

C++ STL vector 底层通过_start_finish_endofstorage 三个指针管理动态数组内存。模拟实现需涵盖构造析构迭代器空间管理及修改操作。关键点在于区分 reserve 与 resize 对容量的影响,处理 memcpy 浅拷贝风险,以及掌握插入删除导致的迭代器失效场景与正确遍历策略。

筑梦师发布于 2026/3/16更新于 2026/7/2837 浏览
C++ STL vector 底层原理与模拟实现

C++ STL vector 底层原理与模拟实现

在之前的讨论中,我们详细了解了 vector 核心接口的使用。接下来,我们将深入剖析其底层实现机制,并通过模拟代码来加深理解。

一、vector 的基本成员变量

在着手模拟之前,必须明确 vector 内部维护了哪些关键指针。通过查阅源码可以发现,vector 主要依赖三个指针来管理内存:

  • _start:指向已分配空间的头部。
  • _finish:指向最后一个有效数据元素的下一个位置。
  • _endofstorage:指向已分配空间的末尾。

基于此,我们可以搭建出 vector 的类框架。需要注意的是,模板类的声明和定义通常不能分离到不同文件中,因此所有实现都放在头文件内。

#include <iostream>
using namespace std;

namespace my_vector {
template<class T>
class vector {
public:
    // 迭代器直接使用原生指针
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    iterator _start;      // 指向空间头部
    iterator _finish;     // 指向最后一个有效数据的下一个位置
    iterator _endofstorage; // 指向空间的末尾
};}

二、vector 核心接口的实现

2.1 构造相关接口

构造函数涵盖了多种初始化场景,包括默认构造、指定数量构造、拷贝构造、初始化列表构造以及迭代器区间构造。赋值运算符重载则通常通过交换逻辑来实现。

// 默认构造
vector() : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {}

// 使用 n 个 val 初始化
// T() 会调用类型 T 的默认构造,支持内置类型或自定义类型
vector(size_t n, T val = T()) {
    resize(n, val);
}

// 拷贝构造
vector(const vector<T>& v) {
    reserve(v.size());
    for (auto e : v) {
        push_back(e);
    }
}

// 初始化列表构造
vector(initializer_list<T> il) {
    reserve(il.size());
    for (auto e : il) {
        push_back(e);
    }
}

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

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

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

// 析构函数
~vector() {
    if (_start) {
        delete[] _start;
        _start = _finish = _endofstorage = nullptr;
    }
}

2.2 迭代器相关的接口实现

vector 的迭代器分为普通迭代器和常量迭代器。反向迭代器则是正向迭代器的逆序版本,rbegin() 返回 end() - 1,rend() 返回 begin() - 1。

iterator begin() { return _start; }
iterator end() { return _finish; }

const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }

2.3 空间相关的接口实现

空间管理是 vector 的核心,涉及 size()、capacity()、empty()、resize() 和 reserve()。其中 reserve 负责扩容但不改变有效数据个数,而 resize 既可能扩容也可能缩容并影响有效数据个数。

size_t size() const { return _finish - _start; }
size_t capacity() const { return _endofstorage - _start; }
bool empty() const { return _start == _finish; }

void resize(size_t n, T val = T()) {
    if (n > size()) {
        reserve(n);
        while (_finish != _start + n) {
            *_finish = val;
            ++_finish;
        }
    } else {
        _finish = _start + n;
    }
}

void reserve(size_t n) {
    auto oldsize = size();
    if (n > capacity()) {
        iterator tmp = new T[n];
        if (_start) {
            // 避免 memcpy 导致的浅拷贝问题,使用赋值操作符进行深拷贝
            for (size_t i = 0; i < oldsize; i++) {
                tmp[i] = _start[i];
            }
            delete[] _start;
        }
        _start = tmp;
        _finish = _start + oldsize;
        _endofstorage = _start + n;
    }
}
关于 memcpy 的浅拷贝问题

在使用 memcpy 复制对象时,如果元素类型包含指针成员,会导致浅拷贝,析构时可能引发重复释放错误。因此,在 reserve 扩容时,建议遍历并使用赋值运算符完成深拷贝。

2.4 元素访问相关的接口实现

最常用的访问方式是下标操作符 operator[]。由于迭代器本质是指针,下标访问直接转化为指针偏移。

T& operator[](size_t i) {
    assert(i < size());
    return _start[i];
}

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

2.5 vector 修改相关的接口实现

插入和删除操作需要特别注意内存管理和迭代器状态。尾插和尾删相对简单,任意位置的插入和删除则涉及数据搬移。

// 尾插
void push_back(const T& x) {
    if (_finish == _endofstorage) {
        reserve(capacity() == 0 ? 4 : 2 * capacity());
    }
    *_finish = x;
    ++_finish;
}

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

// 任意位置插入
iterator insert(iterator pos, const T& x) {
    assert(pos >= _start && pos <= _finish);
    if (_finish == _endofstorage) {
        reserve(capacity() == 0 ? 4 : 2 * capacity());
        // 扩容后原指针失效,需重新计算位置
        pos = _start + (pos - _start);
    }
    iterator end = _finish;
    while (end >= pos) {
        *(end + 1) = *end;
        end--;
    }
    *pos = x;
    ++_finish;
    return pos;
}

// 任意位置删除
iterator erase(iterator pos) {
    assert(pos >= _start && pos <= _finish);
    if (!empty()) {
        iterator it = pos;
        while (it < _finish) {
            *it = *(it + 1);
            ++it;
        }
        --_finish;
    }
    return pos;
}

三、插入删除引起的迭代器失效问题

在进行插入或删除操作时,vector 可能会发生扩容或数据搬移,导致原有迭代器指向的地址无效。

插入导致的失效示例:

void test_vector9() {
    my_vector::vector<int> v{1, 2, 3, 4};
    auto it = v.begin();
    v.push_back(5); // 触发扩容,迭代器失效
    while (it != v.end()) {
        cout << *it << " ";
        *it = 100;
        ++it;
    }
    cout << endl;
}

删除导致的失效示例:

void test_vector10() {
    my_vector::vector<int> v{1, 2, 3, 4, 5, 6};
    auto it = v.begin();
    while (it != v.end()) {
        if (*it % 2 == 0)
            v.erase(it); // erase 返回下一个有效迭代器,但此处未更新
        ++it; // 可能导致越界或野指针
    }
    for (auto e : v) cout << e << " ";
    cout << endl;
}

在删除操作中,如果使用 erase 删除当前元素,应使用 erase 返回的新迭代器继续遍历,或者调整循环逻辑,否则极易引发崩溃。

四、总结

本文梳理了 vector 的底层成员变量、核心接口实现及内存管理机制。重点掌握了 reserve 与 resize 的区别,理解了深拷贝在处理自定义类型时的必要性,并分析了插入删除操作引发的迭代器失效问题。在实际开发中,留意这些细节能有效避免潜在的内存错误。

目录

  1. C++ STL vector 底层原理与模拟实现
  2. 一、vector 的基本成员变量
  3. 二、vector 核心接口的实现
  4. 2.1 构造相关接口
  5. 2.2 迭代器相关的接口实现
  6. 2.3 空间相关的接口实现
  7. 关于 memcpy 的浅拷贝问题
  8. 2.4 元素访问相关的接口实现
  9. 2.5 vector 修改相关的接口实现
  10. 三、插入删除引起的迭代器失效问题
  11. 四、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • HTML 标签详解:网页骨架与语义化布局实战
  • C++ lower_bound 与 upper_bound 核心用法解析
  • OpenClaw Web Search 配置与渠道选择指南
  • SVN Web 管理工具 svnWebUI 部署与权限配置实战
  • Clawdbot 基于云服务器的 AI 助理部署指南
  • Virt-A-Mate v1.22 中文汉化整合版技术解析
  • 零基础学会逻辑回归:从原理到实现
  • 基于 Flutter × HarmonyOS 6.0 的宿舍管理系统:数据结构与架构设计
  • PX4 与 ROS 集成:Offboard 模式解析及轨迹跟踪实战
  • Krita 插件配置与 AI 绘画模型部署指南:故障诊断与维护
  • Llama-3.2V-11B 部署实战:GPU 显存优化与 Batch Size 调优
  • GlobeDiff:基于扩散模型的多智能体部分可观测全局状态推断
  • Fish Speech-1.5 语音风格控制:通过描述词定制音色与语调
  • WEBCPM 技术解析:交互式中文长文本问答框架
  • Claude Code 源码因 Source Map 配置失误泄露,51 万行代码公开
  • Flutter anthropic_sdk_dart 鸿蒙化适配指南及 Claude 集成
  • SpringBoot+Vue+Netty+WebSocket+WebRTC 实现视频聊天
  • 天马 G 前端在安卓掌机上的实战
  • Windows 10/11 下 Codex MCP 服务配置指南:Node.js 安装与 VS Code 调试
  • OpenClaw Web UI 访问报错 Not Found 排查与修复

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online