跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ STL 详解:手写 String 类实现

C++ String 类模拟实现涉及内存管理、深浅拷贝及运算符重载等核心机制。通过自定义命名空间避免冲突,封装迭代器与容量控制函数。重点讲解构造、析构、赋值过程中的资源安全,以及 insert、erase 等修改操作的边界处理。结合 copy-and-swap 惯用法优化赋值效率,确保程序稳定性。

独立开发者发布于 2026/3/16更新于 2026/5/23 浏览
C++ STL 详解:手写 String 类实现

前言

在前面的学习中,我们已经初步掌握了 string 类接口函数的使用方法。本文将带领大家从零开始,逐步实现一个完整的 string 类,深入理解 C++ 内存管理与对象生命周期。

一、String 类总览

为了避免与标准库中的 string 产生命名冲突,我们使用 mystd 命名空间进行封装。

namespace mystd {
class string {
public:
    // 迭代器
    typedef char* iterator;
    typedef const char* const_iterator;

    // 默认成员函数
    string();
    string(const char* str);
    string(const string& s);
    string& operator=(const string& s);
    ~string();

    // 迭代器相关函数
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

    // 容量和大小相关函数
    size_t size();
    size_t capacity();
    void reserve(size_t n);
    void resize(size_t n, char ch = '\0');
    bool empty() const;

    // 修改字符串相关函数
    void push_back(char ch);
    void append(const char* str);
    string& operator+=(char ch);
    string& operator+=(const char* str);
    string& insert(size_t pos, char ch);
    string& insert(size_t pos, const char* str);
    string& erase(size_t pos, size_t len);
    void clear();
    void swap(string& s);
    const char* c_str() const;

    // 访问字符串相关函数
    char& operator[](size_t i);
    const char& operator[](size_t i) const;
    size_t find(char ch, size_t pos = 0) const;
    size_t find(const char* str, size_t pos = 0) const;
    size_t rfind(char ch, size_t pos = npos) const;
    size_t rfind(const char* str, size_t pos = 0) const;

private:
    char* _str;      // 存储字符串
    size_t _size;    // 记录当前有效长度
    size_t _capacity;// 记录当前容量
    static const size_t npos;
};

const size_t string::npos = -1;

// 关系运算符重载函数
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);

// << 和 >> 运算符重载函数
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}

成员变量说明:

  • char* _str:用于存储字符串数据。
  • size_t _size:记录当前字符串的有效长度(不包含 '\0')。
  • size_t _capacity:记录字符串当前的容量(不包含 '\0')。
  • static const size_t npos:静态成员变量,值为整型最大值,通常用作异常返回值。

迭代器说明:

  • typedef char* iterator:普通迭代器,允许读写。
  • typedef const char* const_iterator:常量迭代器,只读。

二、默认成员函数

2.1 无参构造函数

string() : _str(new char[1]{'\0'}), _size(0), _capacity(0) {}

初始化时预留一个 '\0' 作为空字符表示。此时 _size 和 _capacity 均为 0,但实际分配了 1 字节空间存放终止符。

2.2 带参构造函数

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

注意:这里没有使用初始化列表,主要是为了复用 strlen 的计算结果。开辟空间时需额外加 1 以容纳 '\0'。

2.3 拷贝构造函数

在模拟实现前,需明确深浅拷贝的区别:浅拷贝会导致多个对象共享同一块内存,修改一处影响另一处;深拷贝则为每个对象独立分配资源。

传统写法:

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

为拷贝对象分配足够内存,复制内容,确保指针指向不同空间。

现代写法 (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._str); // 调用带参构造创建临时对象
    swap(tmp);
}

利用按值传递参数自动触发深拷贝构造,生成临时对象 tmp,再通过 swap 交换资源。函数结束时 tmp 析构,自动释放原对象的旧内存,无需手动管理。

2.4 赋值运算符重载

同样需要考虑深浅拷贝及自我赋值问题。

传统写法:

string& operator=(const string& other) {
    if (this == &other) return *this;
    delete[] _str;
    _str = new char[other._capacity + 1];
    strcpy(_str, other._str);
    _size = other._size;
    _capacity = other._capacity;
    return *this;
}

关键步骤:防范自我赋值、清理旧资源、深拷贝新资源、返回自身引用支持链式操作。

现代写法 (Copy-and-Swap):

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

参数按值传递,编译器自动调用拷贝构造函数完成深拷贝。进入函数体后执行 swap,将新数据赋予当前对象,局部临时对象 tmp 在析构时清理旧内存。这种方式代码更简洁且异常安全。

2.5 析构函数

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

释放动态分配的内存并重置状态。

三、迭代器

迭代器本质是字符指针,iterator 只是类型别名。

iterator begin() { return _str; }
const_iterator begin() const { return _str; }
iterator end() { return _str + _size; }
const_iterator end() const { return _str + _size; }

begin 返回首字符地址,end 返回最后一个字符后的位置(即 '\0' 的地址)。

四、容量和大小相关函数

4.1 size 与 capacity

直接返回成员变量值,因私有属性无法直接访问。

size_t size() const { return _size; }
size_t capacity() const { return _capacity; }

4.2 reserve

当请求的容量超过当前 capacity 时进行扩容,否则保持不变。

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

一般只扩容不缩容,预留空间给 '\0'。

4.3 empty

判断字符串是否为空。

bool empty() const { return _size == 0; }

五、字符串修改函数

5.1 push_back

尾插字符,若空间不足则扩容。

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

插入字符后必须补充 '\0' 终止符,防止非法访问。

5.2 append

尾插字符串。

void append(const char* s) {
    size_t len = strlen(s);
    if (len + _size > _capacity) {
        size_t newcapacity = (_size + len > _capacity * 2) ? _size + len : _capacity * 2;
        reserve(newcapacity);
    }
    strcpy(_str + _size, s);
    _size += len;
}

待插入字符串本身包含 '\0',无需额外添加。

5.3 operator+=

复用 push_back 和 append 实现。

string& operator+=(char c) {
    this->push_back(c);
    return *this;
}
string& operator+=(const char* s) {
    this->append(s);
    return *this;
}

5.4 insert

在指定位置插入字符或字符串。

插入字符:

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

注意 end 初始化为 _size + 1,避免头部插入时 0 - 1 导致 size_t 下溢死循环。

插入字符串:

void insert(size_t pos, const char* s) {
    assert(pos <= _size);
    size_t len = strlen(s);
    if (len == 0) return;
    if (len + _size > _capacity) {
        size_t newcapacity = len + _size > _capacity * 2 ? len + _size : _capacity * 2;
        reserve(newcapacity);
    }
    size_t end = _size + len;
    while (end > pos + len - 1) {
        _str[end] = _str[end - len];
        --end;
    }
    for (size_t i = 0; i < len; i++) {
        _str[pos + i] = s[i];
    }
    _size += len;
}

先移动尾部字符腾出空间,再填入新字符串。

5.5 erase

删除从 pos 开始的 len 个字符。

void erase(size_t pos, size_t len) {
    assert(pos < _size);
    if (len >= _size - pos) {
        _str[pos] = '\0';
        _size = pos;
    } else {
        size_t begin = pos + len;
        while (begin <= _size) {
            _str[begin - len] = _str[begin];
            ++begin;
        }
        _size -= len;
    }
}

5.6 substr

截取子串。

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

5.7 clear

清空字符串。

void clear() {
    _str[0] = '\0';
    _size = 0;
}

六、访问字符串相关函数

6.1 operator[]

支持下标访问。

char& operator[](size_t pos) {
    assert(pos < _size);
    return _str[pos];
}
const char& operator[](size_t pos) const {
    assert(pos < _size);
    return _str[pos];
}

6.2 find

查找字符或字符串。

size_t find(char c, size_t pos) {
    assert(pos < _size);
    for (size_t i = pos; i < _size; i++) {
        if (_str[i] == c) return i;
    }
    return npos;
}

size_t find(const char* s, size_t pos) {
    assert(pos < _size);
    const char* ptr = strstr(_str, s);
    if (ptr != nullptr) return ptr - _str;
    return npos;
}

七、比较运算符重载

只需重载 < 和 ==,其余可通过组合实现。

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);
}
bool operator>=(const string& s1, const string& s2) {
    return !(s1 < s2);
}
bool operator!=(const string& s1, const string& s2) {
    return !(s1 == s2);
}

八、字符串输入与输出

8.1 >> 运算符重载

跳过前导空白,读取单词。

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

使用缓冲数组减少频繁扩容,遇到空白字符停止。

8.2 << 运算符重载

遍历输出字符。

ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) {
        out << ch;
    }
    return out;
}

目录

  1. 前言
  2. 一、String 类总览
  3. 二、默认成员函数
  4. 2.1 无参构造函数
  5. 2.2 带参构造函数
  6. 2.3 拷贝构造函数
  7. 2.4 赋值运算符重载
  8. 2.5 析构函数
  9. 三、迭代器
  10. 四、容量和大小相关函数
  11. 4.1 size 与 capacity
  12. 4.2 reserve
  13. 4.3 empty
  14. 五、字符串修改函数
  15. 5.1 push_back
  16. 5.2 append
  17. 5.3 operator+=
  18. 5.4 insert
  19. 5.5 erase
  20. 5.6 substr
  21. 5.7 clear
  22. 六、访问字符串相关函数
  23. 6.1 operator[]
  24. 6.2 find
  25. 七、比较运算符重载
  26. 八、字符串输入与输出
  27. 8.1 >> 运算符重载
  28. 8.2 << 运算符重载
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 2024 年大模型方向求职面试经验总结与指南
  • VSCode Copilot 插件卡顿问题解决方案
  • OpenClaw 结合 Qwen3.5 实现本地 AI 助手部署指南
  • Electron 实战:按需构建与打包体积优化
  • 鸣潮 QQ 机器人部署指南:GsCore 核心与 NoneBot 插件配置
  • Mac 平台 Homebrew 安装配置及常用命令详解
  • C 语言快速排序算法详解与优化实现
  • 杭州E类人才认定流程与福利指南
  • 机器学习:聚类分析算法详解
  • Trae AI 原生 IDE 安装与使用指南
  • OpenCLaw Web UI 无法访问 Not Found 问题排查与解决
  • Claude Code 模型参数配置与实战指南
  • 20 个高含金量 Python 毕业设计选题推荐
  • 使用 Ollama 本地部署 Llama 3.1 大模型指南
  • Stable Diffusion WebUI 模型管理:Checkpoint、VAE 及 LoRA 配置实战
  • DGX Spark 部署 Stable Diffusion 3.5 与 ComfyUI 实战
  • Go Web 开发核心理论指南
  • AAAI2025 PC-BEV:多坐标系融合策略实现点云分割加速与精度提升
  • 豆包 AI 视频及图片本地保存方案
  • 基于内网穿透实现本地 WebSocket 服务公网访问

相关免费在线工具

  • 加密/解密文本

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