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

C++ STL string 模拟实现(中)

综述由AI生成本文深入探讨 C++ STL string 类的模拟实现细节,涵盖调整操作与访问操作两大板块。重点解析了 push_back、append、insert、erase 等函数的内存管理与边界处理逻辑,特别是 insert 时的反向移动策略及 erase 中的 npos 用法。同时剖析了 swap 函数为何需要特化以实现零拷贝交换,以及 operator[] 的 const 正确性设计。内容聚焦底层原理与工程实践,帮助理解标准库背后的实现机制。

NodeJser发布于 2026/3/29更新于 2026/6/214 浏览
C++ STL string 模拟实现(中)

调整操作

push_back()

这个函数负责在字符串末尾追加单个字符。核心逻辑在于先判断容量是否充足,如果不够就扩容,最后记得补上结束符 \0。

void string::push_back(char c) {
    // 检查是否需要扩容
    if (_size == _capacity) {
        // 这里采用 2 倍扩容,或者你也可以 1.5 倍扩容
        size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
        reserve(newcapacity);
        _capacity = newcapacity;
    }
    // 插入字符和 \0
    _str[_size++] = c;
    _str[_size] = '\0';
}

append()

append() 有很多重载,我们主要关注追加字符串和追加多个相同字符这两种场景。

追加字符串

需要断言指针不为空,计算长度,判断容量并扩容,最后利用 strcpy 拷贝数据。

void string::append(const char* str) {
    assert(str);
    size_t len = strlen(str);
    // 扩容
    if (_capacity < len + _size) {
        size_t newcapacity = len + _size > 2 * _capacity ? len + _size : 2 * _capacity;
        reserve(newcapacity);
        _capacity = newcapacity;
    }
    // 追加新的数据,strcpy 会拷贝\0,所以不需要手动加
    strcpy(_str + _size, str);
    _size += len;
}

追加多个相同字符

逻辑类似,只是循环写入字符。如果复用了 push_back,就不需要手动加 \0 了。

void string::append(size_t n, char c) {
    // 扩容
    if (_capacity < n + _size) {
        size_t newcapacity = n + _size > 2 * _capacity ? n + _size : 2 * _capacity;
        reserve(newcapacity);
        _capacity = newcapacity;
    }
    // 插入数据
    for (size_t i = 0; i < n; i++) {
        _str[_size++] = c;
    }
    // 如果是对 push_back 进行复用,这里就不用再手动加'\0'了
    _str[_size] = '\0';
}

operator+=()

这个运算符重载有两个功能:插入字符(相当于 push_back)和插入字符串(相当于 append)。为了保证能连续运算,记得返回 *this。

// 追加字符串,复用 append()
string& string::operator+=(const char* str) {
    append(str);
    return *this;
}

// 追加字符,复用 push_back()
string& string::operator+=(char c) {
    push_back(c);
    return *this;
}

insert()

在指定位置插入字符或字符串,需要将后续元素向后移动。这里有个关键点:必须从后往前挪动,否则先挪动的数据会覆盖掉还没处理的数据。

string& string::insert(size_t pos, char c) {
    assert(pos <= _size);
    // 扩容
    if (_capacity < _size + 1) {
        size_t newcapacity = _size + 1 > 2 * _capacity ? 1 + _size : 2 * _capacity;
        reserve(newcapacity);
        _capacity = newcapacity;
    }
    for (size_t i = _size; i > pos; i--) {
        // 不能改成_str[i + 1] = _str[i] 的形式
        _str[i] = _str[i - 1];
    }
    // 记得加'\0'
    _str[++_size] = '\0';
    _str[pos] = c;
    return *this;
}

string& string::insert(size_t pos, const char* str) {
    assert(pos <= _size);
    size_t len = strlen(str);
    // 扩容
    if (_capacity < len + _size) {
        size_t newcapacity = len + _size > 2 * _capacity ? len + _size : 2 * _capacity;
        reserve(newcapacity);
        _capacity = newcapacity;
    }
    // 挪动数据
    for (size_t i = _size + len; i >= len; i--) {
        _str[i] = _str[i - len];
    }
    // 插入字符串
    for (size_t i = 0; i < len; i++) {
        _str[i + pos] = str[i];
    }
    _size += len;
    // 加'\0'
    _str[_size] = '\0';
    return *this;
}

erase()

删除一段数据,本质上就是把后面的数据拉到前面覆盖。同样要注意移动方向,避免覆盖未处理数据。

npos

npos 是一个静态成员常量,对于 size_t 类型具有最大可能的值。当用作长度参数时,表示'直到字符串末尾'。它定义为 -1,因为 size_t 是无符号整型,所以 -1 就是该类型的最大值。

const static size_t npos = -1;

回到 erase 的实现:

string& string::erase(size_t pos, size_t len = npos) {
    // 如果会把 pos 位置之后的所有元素全部删除,就不需要挪动数据,直接在 pos 位置加'\0'就行了
    if (len >= _size - pos) {
        _str[pos] = '\0';
        _size = pos;
    } else {
        // 挪动数据
        // 从 pos + len 位开始向前挪动
        for (size_t i = pos + len; i <= _size; i++) {
            _str[i - len] = _str[i];
        }
        _size -= len;
        // 加'\0'
        _str[_size] = '\0';
    }
    return *this;
}

swap()

尽管算法库中有通用的 swap,但 string 类依然实现了自己的版本,包括成员函数和全局重载。

成员函数 swap

直接交换三个成员变量即可,效率极高。

void string::swap(string& s) {
    // 交换的时候还可以直接使用 std 中的 swap 进行交换
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);
}

全局 swap 重载

为了防止重定义冲突,声明和定义通常分离在 .h 和 .cpp 文件中。放在全局命名空间是为了让 ADL(参数依赖查找)能优先找到它,而不是去搜索模板实例。

// string.h
void swap(test::string& s1, test::string& s2);

// string.cpp
void swap(test::string& s1, test::string& s2) {
    s1.swap(s2); // 调用类的成员函数 swap
}
为什么要实现成员函数 swap

既然算法库有 swap,为什么还要自己写?因为通用模板通常是借助临时变量交换的:

template<class T>
void swap(T& a, T& b) {
    T c(a);
    a = b;
    b = c;
}

对于存储大量数据的 string,这种拷贝开销是巨大的。而自定义 swap 只需交换指针和计数,瞬间完成。所以在标准库中,我们会尽量通过重载来避免使用默认的模板版本。

访问操作

operator[]

下标访问操作符直接返回对应位置元素的引用。需要提供两个重载:非 const 版本返回普通引用,const 版本返回 const 引用,防止权限放大。

char& string::operator[](size_t index) {
    return *(_str + index);
}

// 注意这里构成重载的原因是最后的那个 const,它修饰的是 this 指针
const char& string::operator[](size_t index) const {
    return *(_str + index);
}

front() 和 back()

分别返回首尾元素的引用。出于同样的原因,也要提供 const 版本。

char& string::front() {
    return _str[0];
}

const char& string::front() const {
    return _str[0];
}

char& string::back() {
    return _str[_size - 1];
}

const char& string::back() const {
    return _str[_size - 1];
}

目录

  1. 调整操作
  2. push_back()
  3. append()
  4. operator+=()
  5. insert()
  6. erase()
  7. npos
  8. swap()
  9. 为什么要实现成员函数 swap
  10. 访问操作
  11. operator[]
  12. front() 和 back()
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • CSS 元素显示模式详解:块级、行内及转换
  • 人形机器人 LET 数据集开源:6 万分钟真机实采与多模态技术
  • 前端瀑布流布局:基础实现与高性能优化
  • 基于动态反演和扩展状态观测器的无人机鲁棒反馈线性化自适应姿态控制器
  • VoxCPM-1.5-TTS-WEB-UI 基于镜像站的快速部署方案
  • 用 Selenium 搭建免费 Web 搜索 API 服务
  • OpenClaw 技能精选:为本地 AI 助手打造超级插件市场
  • 空洞卷积(Dilated Convolution)原理与基础架构解析
  • DCU BW1000 使用 llama.cpp 推理 Qwen3-Coder-30B 模型失败记录
  • Vue3 中点击事件方法提示不存在的排查与修复方案
  • LLaMA Factory+QLoRA 微调 70B 模型实测
  • 前端实现视频画中画功能 - 主页面与小窗同步控制
  • OpenClaw 爆火倒逼低代码 AI 变革:从工具赋能到生态重构
  • 深度解析 UGC、PGC、AIGC 等内容生成模式与区别
  • AI 大模型助力专利翻译,妙算翻译亮相国际论坛
  • OpenClaw web_search 与 web_fetch 最佳实践速查
  • 程序员办公室趣谈:雍正专治“八阿哥”Bug
  • MySQL 常用命令速查表:从基础查询到运维优化
  • Stable Diffusion v1.5 创意设计师指南:嵌入 Figma/PS 工作流
  • 量化、算子融合与内存映射:C 语言实现 AI 推理优化

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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