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

C++ STL string 类从零实现详解

C++ 标准库 string 类的底层实现涉及内存管理、深浅拷贝及运算符重载等核心概念。通过手动封装 string 类,可以深入理解动态字符数组的分配策略、扩容机制以及迭代器原理。文章详细展示了构造函数、析构函数、赋值操作符及常用成员函数的具体编码逻辑,重点剖析了深拷贝避免资源冲突的现代写法(copy-and-swap),并补充了输入输出流的重载细节。掌握这些实现有助于夯实 C++ 基础,提升对 RAII 和资源管理的认知。

魔法巫师发布于 2026/3/22更新于 2026/7/2537 浏览
C++ STL string 类从零实现详解

前言

在掌握 string 类接口函数的基本用法后,深入理解其底层实现往往能带来更大的收获。本文将带你从零开始,逐步构建一个完整的 string 类,重点剖析内存管理、深浅拷贝及运算符重载等核心机制。

一、string 类总览

为了演示方便,避免与标准库中的 std::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);
    ;
    ;

    
    ;
    ;
    string& +=( ch);
    string& +=( * str);
    ;
    ;
    ;
    ;
    ;
    ;

    
    & []( i);
     & []( i) ;
    ;
    ;
    ;
    ;

:
    * _str;      
     _size;    
     _capacity;
       npos; 
};

  string::npos = ;


 <( string& s1,  string& s2);
 <=( string& s1,  string& s2);
 >( string& s1,  string& s2);
 >=( string& s1,  string& s2);
 ==( string& s1,  string& s2);
 !=( string& s1,  string& s2);


ostream& <<(ostream& out,  string& s);
istream& >>(istream& in, string& s);
}
void resize(size_t n, char ch = '\0')
bool empty() const
// 修改字符串
void push_back(char ch)
void append(const char* str)
operator
char
operator
const
char
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
const
char
operator
size_t
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
// 存储字符串
size_t
// 记录当前有效长度
size_t
// 记录当前容量
static
const
size_t
// 静态成员变量
const
size_t
-1
// 关系运算符重载
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
// 流操作符重载
operator
const
operator

成员变量说明:

  • _str:字符指针,用于存储实际内容。
  • _size:记录当前字符串的有效长度(不包含 '\0')。
  • _capacity:记录当前分配的容量大小(不包含 '\0')。
  • npos:静态常量,值为整型最大值,通常用作查找失败的返回值。

迭代器说明: 这里直接使用 char* 指针作为迭代器,通过类型别名简化调用。

二、默认成员函数

2.1 无参构造函数

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

初始化时预留一个 '\0' 作为空字符串的表示。此时 _size 和 _capacity 均为 0,但额外为 '\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);
}

这种写法利用按值传递参数自动触发拷贝构造,生成一个安全的临时副本,再通过交换成员变量完成赋值。当函数结束时,临时对象析构,自动释放旧内存,既简洁又安全。

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

关键点在于先检查自我赋值,再释放旧资源,最后深拷贝新数据。返回引用支持链式赋值。

现代写法:

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

这里参数采用按值传递,编译器会自动调用拷贝构造函数创建临时对象 tmp。进入函数体后,直接交换 this 与 tmp 的成员变量。tmp 持有旧数据,函数结束时析构 tmp 即完成了旧资源的清理。这种写法极大地降低了出错风险。

2.5 析构函数

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

负责释放动态分配的内存,防止内存泄漏。

三、迭代器

string 的迭代器本质是字符指针。

typedef char* iterator;
typedef const char* const_iterator;

3.1 begin

返回首字符地址。

iterator begin() { return _str; }
const_iterator begin() const { return _str; }

3.2 end

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

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

四、容量和大小相关函数

4.1 size & capacity

直接返回对应的成员变量值。

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

4.2 reserve

预留空间,仅在请求容量大于当前容量时扩容。

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

4.3 empty

判断字符串是否为空。

bool empty() const { return strcmp(_str, "") == 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+=

复用上述函数实现。

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 > pos,防止 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

删除指定位置的字符。

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

修正了原代码中 string; 的语法错误,改为实例化对象 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;
    } else {
        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;
}

通过手动实现 string 类,我们可以更清晰地看到 C++ 中资源管理的细节。从内存的动态分配到深拷贝的安全保障,再到运算符的重载逻辑,每一个环节都体现了 RAII 的思想。希望这些代码示例能为你的学习提供实质性的帮助。

目录

  1. 前言
  2. 一、string 类总览
  3. 二、默认成员函数
  4. 2.1 无参构造函数
  5. 2.2 带参构造函数
  6. 2.3 拷贝构造函数
  7. 2.4 赋值运算符重载
  8. 2.5 析构函数
  9. 三、迭代器
  10. 3.1 begin
  11. 3.2 end
  12. 四、容量和大小相关函数
  13. 4.1 size & capacity
  14. 4.2 reserve
  15. 4.3 empty
  16. 五、字符串修改函数
  17. 5.1 push_back
  18. 5.2 append
  19. 5.3 operator+=
  20. 5.4 insert
  21. 5.5 erase
  22. 5.6 substr
  23. 5.7 clear
  24. 六、访问字符串相关函数
  25. 6.1 operator[]
  26. 6.2 find
  27. 七、比较运算符重载
  28. 八、字符串输入与输出
  29. 8.1 >> 运算符重载
  30. 8.2 << 运算符重载
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Qwen3.5-9B 如何以 1/13 参数量超越 GPT-oss-120B?架构与性能实测
  • 基于 ESP32-S3 的蓝牙/有线双模智能家居键盘控制器设计详解
  • 单链表综合练习:删除指定值节点、反转链表及查找中间节点
  • Python 自动化办公:使用 xlutils 修改 Excel
  • 使用 Trae IDE 将 Figma 设计稿转换为前端代码
  • 国内五大主流大语言模型对比与使用指南
  • 大疆无人机开发指南:MSDK、PSDK 与上云 API 实战
  • C++ 高精度时间库 chrono 详解
  • Claude Code 与 GitHub Copilot CLI 终端选型实测
  • 2017 年技术类书籍精选推荐
  • SpringBoot 整合 LangChain4j 与 Tavily 实现联网搜索
  • Vivado FPGA 多模块顶层例化技巧与架构设计
  • Python 中文繁简体转换工具 openccpy 使用指南
  • Claude Code、OpenClaw、OpenCode 与 SkillLite 架构深度对比
  • Mac mini M4 部署 OpenClaw + Ollama 本地大模型对接飞书机器人
  • Double DQN 算法原理与 Python 实战实现
  • 前端音频录制工具:AudioRecorder 类实现与使用指南
  • 法奥机器人操作与编程入门指南
  • Python Plotly 数据可视化:从基础到最佳实践
  • Flutter 组件 google_generative_language_api 适配鸿蒙 HarmonyOS 实战

相关免费在线工具

  • 加密/解密文本

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