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

STL string 类源码实现详解

从零模拟实现 C++ STL string 类。涵盖构造函数、拷贝构造与赋值运算符的深拷贝策略(传统写法与 Copy-and-Swap)、迭代器设计、容量管理(reserve/capacity)、字符串修改(push_back/insert/erase)及输入输出流重载。重点解析内存分配机制、自我赋值保护及边界条件处理,帮助深入理解底层数据结构与资源管理。

苹果系统发布于 2026/3/30更新于 2026/7/1930 浏览
STL string 类源码实现详解

概述

在掌握 string 接口后,我们尝试从零实现一个完整的 string 类。为了避免与标准库冲突,这里使用 mystd 命名空间封装。

核心成员与迭代器

类内部主要维护三个私有成员:

  • _str: 指向动态分配的字符数组。
  • _size: 当前有效字符长度(不含 \0)。
  • _capacity: 当前分配的总容量(不含 \0)。 此外定义 npos 为无符号整型最大值,用于表示查找失败等异常状态。 迭代器直接复用指针类型别名,iterator 对应 char*,const_iterator 对应 const char*。

默认成员函数实现

构造函数

无参构造时,需预留一个 \0 作为空串标识,此时 _size 和 _capacity 均为 0。带参构造则根据传入 C 字符串长度计算所需空间,注意额外分配一位给终止符。

// 无参构造
string() : _str(new char[1]{'\0'}), _size(0), _capacity(0) {}

// 带参构造
string(const char* str) {
    _size = strlen(str);
    _capacity = _size;
    _str = new char[_capacity + 1];
    strcpy(_str, str);
}

注:初始化列表顺序遵循成员声明顺序,但此处为了复用 strlen 结果,直接在函数体内计算更为直观。

析构函数

释放动态内存并重置状态。

~string() {
    delete[] _str;
    _size = 0;
    _capacity = 0;
}

深拷贝与赋值策略

拷贝构造函数

浅拷贝会导致多个对象共享同一块内存,析构时引发重复释放错误。必须实现深拷贝,为新对象独立分配内存并复制内容。

传统写法如下:

string(const string& s) : _size(s._size), _capacity(s._size) {
    _str = new [_capacity + ];
    (_str, s._str);
}
char
1
strcpy

现代推荐写法采用 Copy-and-Swap 惯用法,利用临时对象完成深拷贝后再交换,能自动处理资源清理且代码更简洁:

void swap(string& tmp) {
    std::swap(_str, tmp._str);
    std::swap(_size, tmp._size);
    std::swap(_capacity, tmp._capacity);
}

string(const string& s) {
    string tmp(s); // 调用拷贝构造创建临时对象
    swap(tmp);     // 交换资源
}

注意:若未对成员变量赋初值(如 nullptr),在交换前确保临时对象已正确初始化,否则可能导致悬空指针。

赋值运算符重载

同样面临深浅拷贝问题,且需防范自我赋值。 传统方式需先检查 this == &other,再释放旧内存并申请新内存。 Copy-and-Swap 版本通过按值传递参数,隐式触发拷贝构造生成临时副本,随后交换即可,无需手动 delete。

string& operator=(string tmp) {
    swap(tmp);
    return *this;
}

这种写法不仅安全,还天然支持链式赋值。

迭代器与访问

迭代器本质是指针。begin() 返回首字符地址,end() 返回最后一个字符后的位置(即 \0 处)。 下标运算符 operator[] 提供读写与只读两个版本,需断言下标有效性。

iterator begin() { return _str; }
const_iterator end() const { return _str + _size; }
char& operator[](size_t i) { assert(i < _size); return _str[i]; }

容量与大小管理

size() 和 capacity() 分别返回当前长度与容量。 reserve(n) 负责扩容,当请求容量大于当前容量时,重新分配更大内存并拷贝旧数据。一般只扩不缩,避免频繁分配开销。 empty() 判断是否为空,可通过比较 _size 是否为零实现,比调用 strcmp 更高效。

void reserve(size_t n) {
    if (n > _capacity) {
        char* tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}
bool empty() const { return _size == 0; }

字符串修改操作

尾插与追加

push_back(char c) 在末尾添加字符。若空间不足,采用二倍扩容策略。插入后记得补上 \0。 append(const char* s) 追加字符串,逻辑类似,需注意目标缓冲区剩余空间。 operator+= 可复用上述函数简化实现。

void push_back(char c) {
    if (_size == _capacity) {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size++] = c;
    _str[_size] = '\0';
}

插入与删除

insert(pos, ...) 涉及中间位置的数据移动。 插入字符时,从尾部开始逐个向后挪动,腾出位置。注意 size_t 无符号特性,循环条件应设为 end > pos 而非 end >= pos,防止头部插入时 end 减至 0 后发生下溢变成极大正数导致死循环。 插入字符串同理,先移动尾部 \0 及后续字符,再填入新内容。 erase(pos, len) 删除指定范围字符,将后续字符向前覆盖。若删除至末尾,只需截断。

void insert(size_t pos, char c) {
    assert(pos <= _size);
    if (_size == _capacity) {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    size_t end = _size + 1;
    while (end > pos) {
        _str[end] = _str[end - 1];
        --end;
    }
    _str[pos] = c;
    _size++;
}

substr 截取子串,新建 string 对象并填充数据。 clear 仅将首字符置为 \0 并重置长度,不释放内存,效率更高。

string substr(size_t pos, size_t len) {
    assert(pos < _size);
    if (len >= _size - pos) len = _size - pos;
    string tmp;
    tmp.reserve(len);
    for (size_t i = 0; i < len; ++i) {
        tmp += _str[pos + i];
    }
    return tmp;
}

查找与比较

find 支持查找字符或子串。字符查找线性扫描即可;子串查找可利用 strstr 辅助,返回相对偏移量。 关系运算符重载中,只需实现 < 和 ==,其余可通过组合推导得出,减少冗余代码。

bool operator<(const string& s1, const string& s2) {
    return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator==(const string& s1, const string& s2) {
    return strcmp(s1.c_str(), s2.c_str()) == 0;
}

输入输出流

重载 >> 和 << 使 string 能配合标准流使用。 输入时跳过前导空白,读取非空白字符直到遇到分隔符。为避免频繁扩容,可使用局部缓冲数组暂存。 输出时遍历字符逐个写入流。

istream& operator>>(istream& in, string& s) {
    s.clear();
    const int N = 256;
    char buff[N] = { 0 };
    int i = 0;
    char ch = in.get();
    while (ch == ' ' || ch == '\n' || ch == '\t') ch = in.get();
    while (ch != ' ' && ch != '\n' && ch != '\t') {
        buff[i++] = ch;
        if (i == N - 1) { buff[i] = '\0'; s += buff; i = 0; }
        ch = in.get();
    }
    if (i > 0) { buff[i] = '\0'; s += buff; }
    return in;
}
ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) out << ch;
    return out;
}

这样实现了一个功能完备的 string 类,涵盖了内存管理、资源获取即初始化(RAII)思想以及常用算法逻辑。

目录

  1. 概述
  2. 核心成员与迭代器
  3. 默认成员函数实现
  4. 构造函数
  5. 析构函数
  6. 深拷贝与赋值策略
  7. 拷贝构造函数
  8. 赋值运算符重载
  9. 迭代器与访问
  10. 容量与大小管理
  11. 字符串修改操作
  12. 尾插与追加
  13. 插入与删除
  14. 查找与比较
  15. 输入输出流
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Java 核心面试高频题与解析:从基础到分布式架构
  • Stable Diffusion WebUI 时代落幕:ComfyUI 崛起与工具迭代
  • Claude Code 模型配置详解
  • Spring AOP 核心原理与实战指南
  • Docker 拉取镜像失败报错 403 Forbidden 解决方案
  • 大模型学习误区:为何理论与实践需要相结合
  • 两年转行网络安全:真实经验总结与学习路径建议
  • Whisper JAX 大规模音频处理内存优化技巧
  • Ubuntu 22.04 下基于 ROS2 Humble 的 PX4 无人机仿真环境搭建
  • Layui 框架下 Unity WebGL Tab 切换黑屏解决方案
  • Git 原理与使用进阶:远程操作、标签管理与多人协作
  • 无人机飞行空域申请全流程指南
  • 大模型前沿:15 篇必读研究论文汇总
  • Webhook 是什么:原理、实现及缺点分析
  • TeleGrip:基于 VR 的机械臂遥操作系统全流程解析
  • FPGA 实现 CAN 总线:原理、Verilog 代码与硬件验证
  • Python 中国节假日日历库使用指南:识别法定节假日与工作日
  • 利用 GPT 快速梳理 Python 知识脉络与入门指南
  • 多模态 AI 如何重塑人机交互的未来
  • 大模型商业化:ToB 与 ToC 并非选择题

相关免费在线工具

  • 加密/解密文本

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