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

C++ string 类模拟实现

详细讲解了 C++ 标准库 string 类的模拟实现过程。首先分析了浅拷贝与深拷贝的区别及潜在风险,随后深入实现了构造函数、析构函数、赋值运算符重载等核心成员函数。此外还涵盖了 reserve、push_back、insert、erase、find、substr 等常用功能的逻辑实现,以及比较运算符和流操作符的重载。最后提供了完整的头文件与源文件代码示例,帮助理解内存管理与资源控制。

黑客帝国发布于 2026/3/28更新于 2026/10/2396 浏览
C++ string 类模拟实现

C++ string 类模拟实现

1.1 经典的 string 类问题

在面试中,常要求模拟实现 string 类,重点在于构造、拷贝构造、赋值运算符重载及析构函数。以下 String 类的实现存在潜在问题:

// 为了和标准库区分,此处使用 String
class String {
public:
    // String() : _str(new char[1]) { *_str = '\0'; }
    // String(const char* str = "\0") 错误示范
    // String(const char* str = nullptr) 错误示范
    String(const char* str = "") {
        // 构造 String 类对象时,如果传递 nullptr 指针,可以认为程序非
        if (nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    ~String() {
        if (_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

// 测试
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);
}

说明:上述 String 类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的。当用 s1 构造 s2 时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2 共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式称为浅拷贝。

1.2 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

1.3 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

模拟实现

构造函数的模拟实现
// 传统写法
/*string(const string& s) { 
    _str = new char[s._capacity + 1]; 
    strcpy(_str, s._str); 
    _size = s._size; 
    _capacity = s._capacity; 
}*/

// 现代写法
string(const string& s) {
    string tmp(s._str); // 默认构造
    swap(tmp); // 把 tmp 和 s2 交换
}
赋值重载的模拟实现
// s2=s1 赋值
// 传统写法
/*string& operator=(const string& s) { 
    if (this != &s) { 
        delete[] _str; 
        _str = new char[s._capacity + 1]; 
        _size = s._size; 
        _capacity = s._capacity; 
    } 
    return *this; 
}*/

// 最新版本的赋值
string& operator=(string tmp) {
    swap(tmp);
    return *this;
}
析构的模拟实现
~string() {
    delete[] _str;
    _str = nullptr;
    _capacity = _size = 0;
}
clear 的模拟实现
void clear() {
    _str[0] = '\0';
    _size = 0;
}
reserve 的模拟实现
void string::reserve(size_t n) {
    if (n > _capacity) {
        char* tmp = new char[n + 1]; // 永远多开一个空间
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}
push_back 的模拟实现
void string::push_back(char ch) {
    if (_size == _capacity) {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size] = ch;
    ++_size;
    _str[_size] = '\0';
}
append 的模拟实现
void string::append(const char* str) {
    size_t len = strlen(str);
    if (_size + len > _capacity) {
        reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
    }
    strcpy(_str + _size, str); // _str+size 就是\0 的位置
    _size += len;
}
insert 的模拟实现
void string::insert(size_t pos, char ch) // 插字符
{
    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] = ch;
    ++_size;
}

void string::insert(size_t pos, const char* str) // 插字符串
{
    assert(pos <= _size);
    // 扩容
    size_t len = strlen(str);
    if (_size + len > _capacity) {
        reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
    }
    size_t end = _size + len;
    if (len == 0) return;
    while (end > pos + len - 1) // end=pos+len 时也要继续
    {
        _str[end] = _str[end - len];
        --end;
    }
    for (size_t i = 0; i < len; ++i) {
        _str[pos + i] = str[i];
    }
    _size += len;
}
erase 的模拟实现
void string::erase(size_t pos, size_t len) // 缺省参数在声明的时候给
{
    assert(pos < _size);
    if (len > _size - pos) {
        _str[pos] = '\0';
        _size = pos;
    } else {
        for (size_t i = pos + len; i <= _size; i++) {
            _str[i - len] = _str[i];
        }
        _size -= len;
    }
}
find 的模拟实现
size_t string::find(char ch, size_t pos) {
    for (size_t i = pos; i < _size; i++) {
        if (_str[i] == ch)
            return i;
    }
    return npos;
}

size_t string::find(const char* str, size_t pos) {
    assert(pos < _size);
    const char* ptr = strstr(_str + pos, str); // strstr 返回第一个字串位置
    if (ptr == nullptr) {
        return npos;
    } else {
        return ptr - _str;
    }
}
substr 的模拟实现
string string::substr(size_t pos, size_t len) {
    assert(pos < _size);
    // len 太大就调整一下
    if (len > _size - pos) {
        len = _size - pos;
    }
    string sub;
    sub.reserve(len);
    for (size_t i = 0; i < len; ++i) {
        sub += _str[pos + i];
    }
    return sub;
}
比较运算符的模拟实现
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);
}
流插入的模拟实现
ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) {
        out << ch;
    }
    return out;
}
流提取的模拟实现
istream& operator>>(istream& in, string& s) {
    s.clear();
    const int N = 256;
    char buff[N];
    int i = 0;
    char ch;
    // in >> ch; cin 默认提取不到空格跟换行
    ch = in.get();
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == N - 1) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        s += ch;
        // in >> ch;
        ch = in.get();
        if (i > 0) {
            buff[i] = '\0';
            s += buff;
        }
    }
    return in;
}

完整代码

string.cpp
#include"string.h"
namespace bit {
void string::reserve(size_t n) {
    if (n > _capacity) {
        char* tmp = new char[n + 1]; // 永远多开一个空间
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

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

string& string::operator+=(char ch) {
    push_back(ch);
    return *this;
}

void string::append(const char* str) {
    size_t len = strlen(str);
    if (_size + len > _capacity) {
        reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
    }
    strcpy(_str + _size, str); // _str+size 就是\0 的位置
    _size += len;
}

string& string::operator+=(const char* str) {
    append(str);
    return *this;
}

void string::insert(size_t pos, char ch) {
    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] = ch;
    ++_size;
}

void string::insert(size_t pos, const char* str) {
    assert(pos <= _size);
    // 扩容
    size_t len = strlen(str);
    if (_size + len > _capacity) {
        reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
    }
    size_t end = _size + len;
    if (len == 0) return;
    while (end > pos + len - 1) // end=pos+len 时也要继续
    {
        _str[end] = _str[end - len];
        --end;
    }
    for (size_t i = 0; i < len; ++i) {
        _str[pos + i] = str[i];
    }
    _size += len;
}

void string::erase(size_t pos, size_t len) // 缺省参数在声明的时候给
{
    assert(pos < _size);
    if (len > _size - pos) {
        _str[pos] = '\0';
        _size = pos;
    } else {
        for (size_t i = pos + len; i <= _size; i++) {
            _str[i - len] = _str[i];
        }
        _size -= len;
    }
}

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

size_t string::find(const char* str, size_t pos) {
    assert(pos < _size);
    const char* ptr = strstr(_str + pos, str); // strstr 返回第一个字串位置
    if (ptr == nullptr) {
        return npos;
    } else {
        return ptr - _str;
    }
}

string string::substr(size_t pos, size_t len) {
    assert(pos < _size);
    // len 太大就调整一下
    if (len > _size - pos) {
        len = _size - pos;
    }
    string sub;
    sub.reserve(len);
    for (size_t i = 0; i < len; ++i) {
        sub += _str[pos + i];
    }
    return sub;
}

void test_string1() {
    string s1;
    string s2("hello world");
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    for (size_t i = 0; i < s2.size(); ++i) {
        s2[i] += 2;
    }
    cout << s2.c_str() << endl;
    for (auto e : s2) {
        cout << e << " ";
    }
    cout << endl;
    string::iterator it = s2.begin();
    while (it != s2.end()) {
        cout << *it << " ";
        ++it;
    }
}

void test_string2() {
    string s1("hello wwww");
    s1 += 'x';
    cout << s1.c_str() << endl;
    s1.insert(3, "sss");
    cout << s1.c_str() << endl;
    s1.erase(3, 2);
    cout << s1.c_str() << endl;
}

void test_string3() {
    string s("666.cpp.zip");
    size_t pos = s.find('.');
    string suffix = s.substr(pos);
    cout << suffix.c_str() << endl;
    string copy(s);
    cout << copy.c_str() << endl;
}

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

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

istream& operator>>(istream& in, string& s) {
    s.clear();
    const int N = 256;
    char buff[N];
    int i = 0;
    char ch;
    // in >> ch; cin 默认提取不到空格跟换行
    ch = in.get();
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == N - 1) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        s += ch;
        // in >> ch;
        ch = in.get();
        if (i > 0) {
            buff[i] = '\0';
            s += buff;
        }
    }
    return in;
}

void test_string4() {
    string s1("hello");
    cin >> s1;
    cout << s1 << endl;
    cout << "he";
}

int main() {
    bit::test_string4();
}
}
string.h
#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;

namespace bit {
class string {
public:
    // string()
    // : _str(new char[1]{'\0'})
    // 不能给 nullptr 因为会对空指针解引用
    // , _size(0)
    // ,_capacity(0)
    {}

    // 短小频繁调用的函数,可以直接放到类里面,默认是 inline
    typedef char* iterator; // 迭代器就是对各类型的封装
    iterator begin() {
        return _str;
    }
    iterator end() {
        return _str + _size;
    }

    typedef const char* const_iterator;
    const_iterator begin() const {
        return _str;
    }
    const_iterator end() const {
        return _str + _size;
    }

    size_t capacity() const {
        return _capacity;
    }

    void reserve(size_t n);
    void push_back(char ch);
    void append(const char* str);
    string& operator+=(char ch);
    string& operator+=(const char* str);
    void insert(size_t pos, char ch);
    void insert(size_t pos, const char* str);
    void erase(size_t pos, size_t len = npos);
    size_t find(char ch, size_t pos = 0);
    size_t find(const char* str, size_t pos = 0);
    string substr(size_t pos = 0, size_t len = npos);

    string(const char* str = "") // ""就相当于一个\0
    {
        _size = strlen(str);
        _capacity = _size; // capacity 不加 1,因为 capacity 不包含\0
        _str = new char[_capacity + 1]; // 空间多开一个放\0
        strcpy(_str, str); // 会把\0 也拷贝过去
    }

    // s2(s1) 拷贝构造
    // 传统写法
    /*string(const string& s) { 
        _str = new char[s._capacity + 1]; 
        strcpy(_str, s._str); 
        _size = s._size; 
        _capacity = s._capacity; 
    }*/

    // s2(s1) tmp 是 s2
    // 现代写法
    string(const string& s) {
        string tmp(s._str); // 默认构造
        swap(tmp); // 把 tmp 和 s2 交换
    }

    // s2=s1 赋值
    // 传统写法
    /*string& operator=(const string& s) { 
        if (this != &s) { 
            delete[] _str; 
            _str = new char[s._capacity + 1]; 
            _size = s._size; 
            _capacity = s._capacity; 
        } 
        return *this; 
    }*/

    // s1=s3
    // string& operator=(const string& s)
    // {
    //     if (this != &s)
    //     {
    //         //把 s3 的内容都给 tmp.然后 s1 与 tmp 交换
    //         //string tmp(s._str); 调用默认构造
    //         string tmp(s); //调用拷贝构造
    //         swap(tmp);
    //     }
    //     //而且 tmp 出了作用域自动销毁
    //     return *this;
    // }

    // 最新版本的赋值
    string& operator=(string tmp) // 拷贝构造必须用引用,不然会无穷递归 但赋值不用
    {
        swap(tmp);
        return *this;
    }

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

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

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

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

    size_t size() const {
        return _size;
    }

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

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

private:
    char* _str = nullptr;
    size_t _size = 0;
    size_t _capacity = 0;

    // 下列这也算定义
    static const size_t npos = -1; // 静态成员类外初始化,但是这里可以也直接给,static const 才可以在这里直接初始化,而且只有整型可以这么玩
};
// const size_t npos = -1;
}

目录

  1. C++ string 类模拟实现
  2. 1.1 经典的 string 类问题
  3. 1.2 浅拷贝
  4. 1.3 深拷贝
  5. 模拟实现
  6. 构造函数的模拟实现
  7. 赋值重载的模拟实现
  8. 析构的模拟实现
  9. clear 的模拟实现
  10. reserve 的模拟实现
  11. push_back 的模拟实现
  12. append 的模拟实现
  13. insert 的模拟实现
  14. erase 的模拟实现
  15. find 的模拟实现
  16. substr 的模拟实现
  17. 比较运算符的模拟实现
  18. 流插入的模拟实现
  19. 流提取的模拟实现
  20. 完整代码
  21. string.cpp
  22. string.h

更多推荐文章

查看全部
  • Lit 与 Alpine.js:轻量级前端开发的两种路径
  • C++原子操作:从底层原理到实战应用
  • Spring AI 使用 MySQL 持久化 ChatMemory 实战
  • Windows 部署 OpenClaw:本地 AI 助手搭建与公网访问指南
  • Stable Diffusion 基础:ControlNet 人体姿势控制详解
  • 四大 AI 编程工具深度对比:TRAE、Qoder、Cursor 与 Copilot 选型指南
  • Mac Mini M4 本地 AI 模型实战:从 Ollama 到 Stable Diffusion 配置指南
  • Python 编程从入门到精通:语法、数据结构与高级应用指南
  • 直线扫描转换算法(DDA、中点、Bresenham)实现
  • Python 多进程共享变量实现方法
  • N46Whisper 实战:利用云端 AI 快速生成日语视频字幕
  • Effective C++ 读书笔记:C++ 编程习惯与最佳实践
  • 边缘计算实战:基于 LLaMA-Factory 微调模型部署至 Jetson
  • IDEA 配置 Tomcat 服务器详细教程
  • 滑动窗口算法:找到字符串中所有字母异位词
  • PCL 点云处理算法与功能索引(C++ 版)
  • 字节开源 Hyper-SD:Stable Diffusion 1 步高清加速模型详解
  • Java 垃圾回收机制详解
  • Flutter serial 库在鸿蒙系统的适配指南与串口通信实战
  • HY-Motion 1.0 基于 Windows WSL2 部署 Gradio WebUI 全流程

相关免费在线工具

  • 加密/解密文本

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