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

C++ STL string 模拟实现(下):字符串操作与运算符重载

C++ STL string 模拟实现深入解析,涵盖 c_str/data 接口、find 查找算法、迭代器设计、运算符重载策略及写时拷贝机制。通过对比不同实现方案优劣,剖析现代 C++ 开发中的复用思想与性能权衡,帮助开发者理解底层原理并掌握高效编码技巧。

DevOpsTeam发布于 2026/3/28更新于 2026/8/2339 浏览
C++ STL string 模拟实现(下):字符串操作与运算符重载

7. 字符串操作

7.1 c_str() 和 data()

const char* c_str() const;

该函数返回 string 对象底层字符数组的指针,且通过返回的指针不能修改字符串内容。

实现非常简单,直接返回内部指针即可:

const char* string::c_str() const {
    return _str;
}

这里有个关键点需要注意:如果你获取了返回值并存储下来,后续对 string 对象进行了可能导致内存重新分配的操作(如追加、插入),那么之前存储的指针就会失效,变成野指针。这个接口主要是为了兼容那些只接受 C 风格字符串的旧接口。

data() 接口的效果与 c_str() 基本一致,但在某些标准版本中行为略有差异,通常建议优先使用 c_str() 以确保兼容性。

7.2 find()

find() 提供了多种重载形式:

size_t find(const string& str, size_t pos = 0) const;
size_t find(const char* s, size_t pos = 0) const;
size_t find(const char* s, size_t pos, size_t n) const;
size_t find(char c, size_t pos = 0) const;

虽然重载很多,但核心逻辑一致:从 pos 位置开始向后查找,直到字符串尾部。找到第一个匹配项则返回起始位置索引,否则返回 npos。

我们重点模拟第 2 个和第 4 个重载,其他思路类似:

size_t string::find(char c, size_t pos) const {
    assert(pos <= _size); // 防止越界
    for (size_t i = pos; i < _size; ++i) {
        if (_str[i] == c)
            return i;
    }
    return npos;
}

size_t string::find(const char* s, size_t pos) const {
    assert(pos <= _size);
    // 利用 C 库函数 strstr 进行子串匹配
    char* tmp = strstr(_str + pos, s);
    if (tmp == nullptr)
        return npos;
    return tmp - _str;
}

对于带长度参数 n 的重载,可以先截取前 n 个字符再执行相同逻辑。

此外,还有几个功能相似的函数:

  • rfind():从后往前查找。
  • find_first_of() / find_last_of():查找包含指定字符集合中的任意字符。
  • find_first_not_of() / find_last_not_of():查找不包含指定字符集合的字符。

这些实现都与 find() 大同小异,读者可以尝试自行实现以加深理解。

8. 迭代器相关

在之前的章节中提到过,可以直接用 typedef char* iterator; 来模拟迭代器。这里做进一步补充,特别是关于 const 迭代器的处理。

8.1 迭代器实现

普通迭代器允许修改数据,但对于 const string 对象,如果返回普通迭代器会导致权限放大。因此需要区分普通迭代器和常量迭代器:

typedef char* iterator;
typedef const char* const_iterator;

注意不要随意修改类型名称。普通迭代器解引用后可修改数据,而 const_iterator 只能读取,不能修改。

8.2 begin()

返回指向首元素的迭代器。需要提供两个版本:

iterator string::begin() {
    return _str;
}

const_iterator string::begin() const {
    return _str;
}

8.3 end()

返回指向最后一个元素下一个位置的迭代器,即区间右边界。同样提供两个版本:

iterator string::end() {
    return _str + _size;
}

const_iterator string::end() const {
    return _str + _size;
}

记住迭代器区间是左闭右开的,所以 end() 不指向有效数据。rbegin() 和 rend() 的实现相对复杂,涉及反向迭代器逻辑,此处暂不展开。

9. 运算符重载

9.1 流插入和流提取

流操作符必须重载为全局函数或友元函数。

std::ostream& operator<<(std::ostream& out, const string& s);
std::istream& operator>>(std::istream& in, string& s);
  1. 流插入 (<<):将 _str 的内容写入流中。
ostream& operator<<(ostream& out, const string& s) {
    for (size_t i = 0; i < s.size(); ++i) {
        out << s[i];
    }
    return out;
}
  1. 流提取 (>>):由于不知道输入长度,不能直接用 cin >> _str。策略是逐个读取字符,遇到空格或换行停止。为了性能,可以引入临时缓冲区,避免频繁扩容。
istream& operator>>(istream& in, string& s) {
    char ch = '\0';
    s.clear(); // 清空原有数据
    
    // 先读一个字符,避免空输入问题
    in.get(ch);
    
    char tmp[256];
    int times = 0;
    
    while (ch != '\n' && ch != ' ') {
        if (times == 255) {
            tmp[times] = '\0';
            s += tmp;
            times = 0;
        }
        tmp[times++] = ch;
        in.get(ch);
    }
    
    // 处理剩余字符
    if (times != 0) {
        tmp[times] = '\0';
        s += tmp;
    }
    return in;
}

9.2 比较运算符

需要实现 <, <=, >, >=, ==, !=。实际上只需实现 == 和 >,其余可复用:

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;
}

bool operator<(const string& s1, const string& s2) {
    return !(s1 == s2 || s1 > s2);
}

bool operator<=(const string& s1, const string& s2) {
    return s1 < s2 || s1 == s2;
}

bool operator>=(const string& s1, const string& s2) {
    return !(s1 < s2);
}

bool operator!=(const string& s1, const string& s2) {
    return !(s1 == s2);
}

10. string 模拟实现的现代写法

赋值运算符有两种常见写法:

// 写法一:基于交换(Move Semantics 思想)
String& operator=(String s) {
    swap(_str, s._str);
    return *this;
}

// 写法二:传统深拷贝
string& string::operator=(const string& s) {
    string tmp(*this);
    clear();
    if (s._size > _capacity) {
        size_t newcapacity = 2 * _capacity > s._size ? 2 * _capacity : s._size;
        reserve(newcapacity);
    }
    strcpy(_str, s._str);
    return *this;
}

从性能角度看,写法一更优,因为它避免了不必要的深拷贝。但在面试快速搭建框架时,写法二逻辑更直观。现代 C++ 开发倾向于复用思想,减少 Bug 风险,除非对性能有极致要求,否则推荐采用类似写法一的优化策略。

本文多处已应用了这种现代写法,例如比较运算符和赋值操作。

11. 补充阅读:写时拷贝

在某些编译器环境(如 g++)中,string 可能采用写时拷贝(Copy-On-Write, CoW)机制。

原理如下:

  1. 每个 string 对象维护一个共享计数器 time。
  2. 发生拷贝时,执行浅拷贝,计数器 time++。
  3. 析构时 time--,仅当 time 为 0 时才释放空间。
  4. 若修改数据前发现 time > 1,则触发深拷贝,当前对象独占新空间。

这种方式在多个对象共享同一块内存且不修改的场景下效率极高。但也存在缺陷,例如多线程环境下计数器的同步开销等问题,实际应用中需权衡利弊。

目录

  1. 7. 字符串操作
  2. 7.1 c_str() 和 data()
  3. 7.2 find()
  4. 8. 迭代器相关
  5. 8.1 迭代器实现
  6. 8.2 begin()
  7. 8.3 end()
  8. 9. 运算符重载
  9. 9.1 流插入和流提取
  10. 9.2 比较运算符
  11. 10. string 模拟实现的现代写法
  12. 11. 补充阅读:写时拷贝
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Flutter 鸿蒙适配 mediapipe_core:端侧 AI 推理与手势识别实战
  • AI 核心概念解析:Skill、MCP 与 Function Call
  • Java 字符处理:char、String 与 StringBuilder 详解
  • 使用 Higress 将 REST API 转换为 MCP Server 工具
  • Python 自动化脚本:实现定时与循环发送消息
  • 员工管理系统设计与实现
  • Kimi 高级用法教程:从入门到 AI 应用实战
  • Android 面试经验复盘与核心知识点梳理
  • C++ 二叉搜索树:原理与增删查实现详解
  • C++ 入门:引用、内联函数与 nullptr 详解
  • 基于 C# 的 PLC 转 Web API 服务器框架实现
  • 基于冠豪猪优化算法的无人机三维路径规划与 Matlab 实现
  • Robo-Diffusion 机器人图像生成使用技巧
  • 基于 RetinaFace 与 CurricularFace 的身份核验系统实现
  • 深入解剖 STL map/multimap:接口使用与核心特性详解
  • AVL 树核心详解:从概念到 C++ 手撕实现
  • AR 远程协作中的深度感知:基于 lingbot-depth-pretrain-vitl-14 的空间锚点实现
  • 大疆无人机如何导出日志并解析
  • Telegram Android 官方开源客户端源码解析与定制指南
  • Python GIS 脚本编程:CARTO、Mapbox 与 Hadoop 实战

相关免费在线工具

  • 加密/解密文本

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