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

C++ string 类模拟实现详解

C++ string 类模拟实现主要解决浅拷贝导致的内存重复释放问题,通过深拷贝确保资源独立。内容涵盖构造函数、析构函数、赋值运算符重载及常用成员函数如 push_back、append、insert 等的具体逻辑。重点展示了如何管理动态内存、处理扩容策略以及实现比较与流操作符,为理解 C++ 标准库底层机制提供参考。

beaabea发布于 2025/12/9更新于 2026/10/2194 浏览
C++ string 类模拟实现详解

C++ string 类模拟实现

1.1 经典的 string 类问题

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

// 为了和标准库区分,此处使用 String
struct String {
public:
    // 构造 String 类对象时,如果传递 nullptr 指针,可以认为程序非空
    String(const char* str = "") {
        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) {
    string tmp(s._str); // 默认构造
    swap(tmp);          // 把 tmp 和 s2 交换
}

赋值重载的模拟实现

// 最新版本的赋值
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) {
        _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);
    if (ptr == nullptr) {
        return npos;
    } else {
        return ptr - _str;
    }
}

substr 的模拟实现

string string::substr(size_t pos, size_t len) {
    assert(pos < _size);
    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;
    ch = in.get();
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == N - 1) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        s += 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);
    _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) {
        _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);
    if (ptr == nullptr) {
        return npos;
    } else {
        return ptr - _str;
    }
}

string string::substr(size_t pos, size_t len) {
    assert(pos < _size);
    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;
    ch = in.get();
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == N - 1) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        s += 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:
    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 = "") {
        _size = strlen(str);
        _capacity = _size;
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }

    string(const string& s) {
        string tmp(s._str);
        swap(tmp);
    }

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

目录

  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

更多推荐文章

查看全部
  • Vivado 2023.2 下载安装与 FPGA 开发环境搭建指南
  • VS Code 中 GitHub Copilot 无法自定义模型 API
  • 大模型、AI 大模型与 GPT 模型详解
  • ClawX:OpenClaw 可视化桌面客户端实战指南
  • OpenClaw 本地 AI 智能体安装与配置指南
  • ClickHouse 招聘研发经理及 C++ 软件工程师 (chDB)
  • WiFi模块AT指令全解析和智能家居APP制作
  • Apache SeaTunnel Web 部署及可视化数据集成实践
  • 腾讯混元大模型辅助开发自助棋牌室平台实战
  • Mac Intel 本地 LLM 部署实践:Ollama、Transformers 与 OpenVINO 对比
  • 二叉搜索树:概念、性能与实现
  • 大语言模型提示词编写与应用指南
  • 8 个 AI 平台的速度和 Token 消耗实测
  • Circle Loss:统一 Softmax 与 Triplet,从线性到圆形的优化视角
  • AI 技能(Skills):面向任务自动化的模块化执行范式
  • C++ 容器适配器与核心数据结构精解:栈、队列、Deque 底层实现与实战
  • C++ 模板进阶:非类型参数、特化与分离编译
  • 《Agent Runtime 工程化》第十章 Eval Harness 与 CI 回归:10.5 flaky eval
  • 无人机路径规划算法详解
  • 【实战源码】TeleGrip:基于VR的机械臂遥操作系统全流程解析

相关免费在线工具

  • 加密/解密文本

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