模拟实现C++中的string类型:从底层理解字符串操作

string

  • 前言
  • 核心成员变量设计
  • 构造函数与析构函数
    • 默认构造函数
    • 从C风格字符串构造
    • 填充构造
    • 拷贝构造函数
    • 迭代器范围构造
    • 析构函数
  • 基本操作实现
    • 迭代器支持
    • 容量管理
    • 元素访问
  • 字符串修改操作
    • 拼接操作
    • 插入与删除
  • 字符串查找操作
  • 运算符重载
  • 总结
  • 每文推荐


前言

在C++中,std::string是我们日常开发中频繁使用的字符串类,它封装了复杂的内存管理和字符串操作细节。今天我们来手动模拟实现一个简化版的string类,深入理解其底层工作原理。


核心成员变量设计

一个基础的string类需要包含三个核心成员变量:

private:char* _a = new char[1] {'\0'};  // 存储字符串的字符数组size_t _size = 0;               // 当前字符串长度size_t _capacity = 0;           // 当前容量(可容纳的最大字符数,不含终止符)
  • _a:指向动态分配的字符数组,用于存储字符串内容
  • _size:记录当前字符串中有效字符的数量
  • _capacity:记录当前分配的内存可容纳的最大字符数(不包括末尾的’\0’)

构造函数与析构函数

默认构造函数

默认构造一个空字符串:

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

从C风格字符串构造

通过const char*构造string,需要先计算字符串长度,再分配内存并复制内容:

string::string(const char* s)
{size_t n = strlen(s);reserve(n);  // 预留足够空间*this += s;  // 使用+=操作符复制内容
}

填充构造

创建包含n个字符c的字符串:

string::string(size_t n, char c)
{reserve(n);while (n--){*this += c;}
}

拷贝构造函数

实现深拷贝,避免浅拷贝导致的内存问题:

string::string(const string& str)
{string tmp = str._a;  // 利用已有的构造函数swap(tmp);            // 交换当前对象和临时对象的资源
}

迭代器范围构造

通过迭代器范围构造字符串:

template <class InputIterator>
string::string(InputIterator first, InputIterator last)
{reserve(last - first);  // 预留足够空间while (first != last){*this += *first;first++;}
}

析构函数

释放动态分配的内存:

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

基本操作实现

迭代器支持

实现begin和end迭代器,方便使用范围for循环:

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

容量管理

reserve函数用于预留内存空间,避免频繁的内存分配:

void string::reserve(size_t n)
{if (_capacity <= n){_capacity = n;char* tmp = new char[_capacity + 1] {};  // +1 用于存储终止符'\0'memcpy(tmp, _a, _size);delete[] _a;_a = tmp;}
}

resize函数用于调整字符串长度:

void string::resize(size_t n)
{if (_capacity < n){char* tmp = new char[n + 1] {};memcpy(tmp, _a, _size);delete[] _a;_a = tmp;_capacity = n;}_size = n;_a[_size] = '\0';
}// 带填充字符的重载版本
void string::resize(size_t n, char c)
{// 实现略...
}

元素访问

重载[]操作符实现元素访问:

char& operator[](size_t pos)
{assert(_size > pos);  // 边界检查return _a[pos];
}const char& operator[](size_t pos) const
{assert(_size > pos);return _a[pos];
}

字符串修改操作

拼接操作

实现+=操作符和append方法:

string& string::operator+=(const string& str)
{for (size_t i = 0; i < str._size; i++){if (_size == _capacity){// 容量不足时扩容,翻倍增长_capacity = _capacity == 0 ? 4 : _capacity * 2;reserve(_capacity);}_a[_size] = str[i];_size++;}_a[_size] = '\0';return *this;
}// 其他重载版本:+= const char*、+= char
// append方法通过调用+=实现
string& string::append(const string& str)
{*this += str;return *this;
}

插入与删除

实现inserterase方法:

string& string::insert(size_t pos, const string& str)
{assert(pos <= _size);if (_size + str.size() > _capacity){reserve(_size + str.size());  // 确保有足够空间}// 移动现有字符为新内容腾出空间memmove(_a + pos + str._size, _a + pos, _size - pos);// 复制新内容for (size_t i = 0; i < str._size; i++){_a[pos + i] = str[i];}_size += str.size();return *this;
}string& string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len > _size || pos + len > _size){// 删除到末尾_a[pos] = '\0';_size = pos;return *this;}// 移动后面的字符覆盖被删除的部分while (pos + len < _size){_a[pos] = _a[pos + len];pos++;}_size -= len;_a[_size] = '\0';return *this;
}

字符串查找操作

实现findfind_first_of系列方法:

size_t string::find(const string& str, size_t pos) const
{for (size_t i = pos; i < _size; i++){if (_a[i] == str[0]){size_t j = 1;for (j = 1; j < str._size && (i + j) < _size; j++){if (_a[i + j] != str[j]){break;}}if (j == str._size){return i;  // 找到匹配,返回起始位置}}}return npos;  // 未找到,返回npos
}

运算符重载

重载比较运算符:

bool operator== (const string& lhs, const string& rhs)
{if (strcmp(lhs._a, rhs._a) == 0){return true;}return false;
}bool operator> (const string& lhs, const string& rhs)
{if (strcmp(lhs._a, rhs._a) > 0){return true;}return false;
}// 其他比较运算符(!=, >=, <, <=)可通过上述运算符组合实现

重载输入输出运算符:

ostream& operator<< (ostream& os, const string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}istream& operator>> (istream& is, string& str)
{str.clear();char buffer[64] = { 0 };int i = 0;char c = is.get();while (c != ' ' && c != '\n')  // 遇到空格或换行符停止{buffer[i++] = c;if (i == 63)  // 缓冲区满,先存入string{buffer[i] = '\0';str.reserve(str.capacity() + 64);str += buffer;i = 0;}c = is.get();}if (i != 0)  // 处理剩余字符{buffer[i] = '\0';str += buffer;}return is;
}

总结

通过手动实现string类,我们深入理解了动态内存管理、字符串操作的底层实现以及类设计的基本原理。这个简化版的string包含了标准库string的核心功能,包括构造/析构、容量管理、元素访问、字符串修改和查找等操作。

实际的std::string实现会更加复杂,还会考虑异常安全性、小字符串优化(SSO)、多线程安全等问题,但核心思想是一致的。理解这些底层实现有助于我们更好地使用标准库,写出更高效的代码。

如需源码,可在我的gitee上找到,下面是链接:
string
如对您有所帮助,可以来个三连,感谢大家的支持。


每文推荐

Aimer–Ref:rain
张碧晨–光的方向
张杰–追风赶月的人

学技术学累了时可以听歌放松一下。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97428.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pdf转ofd之移花接木

文章目录1.pdf转ofd的方法1.1 spire.pdf.free1.2 ofdrw2.移花接木3.总结1.pdf转ofd的方法 1.1 spire.pdf.free 这个是一个半开源的类库&#xff0c;免费版本的在转换的时候会有一个10的限制&#xff0c;所以不推荐使用&#xff0c;具体教程网上都有&#xff0c;这里只是分享有…

用【Coze】实现文案提取+创作

在AI技术飞速发展的当下&#xff0c;打造专属智能应用成为不少人的向往。今天&#xff0c;就带大家走进字节跳动的扣子Coze平台&#xff0c;看看如何借助它搭建智能体&#xff0c;还会介绍AI工作流&#xff0c;以及详细的Coze搭建步骤&#xff0c;开启你的AI创作之旅&#xff5…

buuctf——web刷题第5页

第五页 目录 [EIS 2019]EzPOP [WMCTF2020]Make PHP Great Again 2.0 [BSidesCF 2020]Hurdles [安洵杯 2019]iamthinking [GWCTF 2019]mypassword [HFCTF2020]BabyUpload [NewStarCTF 2023 公开赛道]include 0。0 [SWPU2019]Web4 [PASECA2019]honey_shop [Black Watc…

果蔬采摘机器人:自动驾驶融合视觉识别,精准定位,高效作业

在智慧农业的快速发展中&#xff0c;果蔬采摘机器人以其自动驾驶技术与视觉识别技术的完美融合&#xff0c;正逐步成为农业生产中的重要力量。这些机器人不仅实现了对果蔬的精准定位&#xff0c;还显著提高了采摘效率&#xff0c;展现了强大的技术优势。一、自动驾驶技术的引领…

2025年职业发展关键证书分析:提升专业能力的路径选择

在当今职场环境中&#xff0c;专业能力的提升已成为职业发展的重要方面。各类专业证书作为系统学习与能力验证的方式&#xff0c;受到越来越多职场人士的关注。本文基于当前行业发展趋势&#xff0c;分析8个在不同领域具有代表性的专业资格认证&#xff0c;为职场人士提供参考信…

【Qt】QCryptographicHash 设置密钥(Key)

QCryptographicHash 本身不能设置密钥&#xff08;Key&#xff09;。 它是一个用于计算非密钥型加密哈希的函数&#xff0c;其设计目的和 HMAC 或加密算法完全不同。 下面我详细解释为什么&#xff0c;以及如何正确地实现你可能想要的功能。 1. QCryptographicHash 的核心功能&…

2025板材十大品牌客观评估报告—客观分析(三方验证权威数据)

随着消费者环保意识提升&#xff0c;板材作为家装基础材料的性能指标受到广泛关注。中国林产工业协会数据显示&#xff0c;2025年国内环保板材市场规模约860亿元&#xff0c;但行业标准执行不一&#xff0c;消费者面临信息不对称问题。本报告严格依据可验证的第三方数据&#x…

诊断通信管理(Diagnostic Communication Management)详解

—— 基于《Specification of Diagnostics AUTOSAR AP R24-11》(SWS_Diagnostics.pdf) 诊断通信管理(Diagnostic Communication Management)是 AUTOSAR 自适应平台诊断管理(Diagnostic Management,DM)的核心功能模块之一,位于应用层,承担 “诊断客户端与诊断服务器实…

vue拖动排序,vue使用 HTML5 的draggable拖放 API实现内容拖并排序,并更新数组数据

vue拖动排序&#xff0c;vue使用 HTML5 的draggable拖放 API实现内容拖并排序&#xff0c;并更新数组数据 vue使用 HTML5 的draggable拖放 API实现内容拖并排序&#xff0c;并更新数组数据 实现效果实现代码1.模板部分2. 添加拖拽相关方法3. 在 data 中添加拖拽状态变量4. 添加…

行内元素块元素

<!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>显示例子</title><style>/* 将行内元素…

算法---动态规划(持续更新学习)

1.动态规划的经典问题 &#xff08;1&#xff09;动规基础&#xff1a;爬楼梯、斐波那契数列 &#xff08;2&#xff09;背包问题&#xff1a;0-1背包&#xff0c;多重背包 &#xff08;3&#xff09;打家劫舍 &#xff08;4&#xff09;股票问题 &#xff08;5&#xff09;子序…

迅睿CMS自定义网站表单:HTML方式调用Select下拉选项数据指南

在迅睿CMS中&#xff0c;当我们需要自定义网站表单并希望以HTML方式调用select下拉选项数据时&#xff08;而非使用系统默认的{$myfield}、{$diyfield}或{$sysfield}模板变量&#xff09;&#xff0c;可以采用以下方法实现。 问题背景 默认情况下&#xff0c;迅睿CMS表单字段通…

k8s--efk日志收集

目录 环境准备 下载efk软件包 下载 nfs 设置nfs开机自启 创建共享存储目录 配置共享目录文件 加载nfs 使共享目录生效 查看 node节点验证 共享目录配置成功 进入efk配置文件目录 修改deployment.yaml文件 修改为master主节点ip 修改为nfs共享存储目录 修改 kibana …

数值分析——算法的稳定性

由于计算时&#xff0c;误差会有累积&#xff0c;如果是长时间的计算&#xff0c;就会影响最后得到的结果&#xff0c;因此&#xff0c;需要分析一下误差的影响能否控制&#xff0c;由此就引出了算法的稳定性 数值的稳定性 对于某一种算法&#xff0c;如果初始值有很小的误差&a…

解密 Kotlin 中的隐藏调度器:Dispatchers.Main.immediate

在日常的 Android 开发中&#xff0c;我们经常使用协程来处理异步任务。你可能已经熟悉了 Dispatchers.Main、Dispatchers.IO 和 Dispatchers.Default&#xff0c;但今天我要介绍一个不太为人知却极其有用的调度器&#xff1a;Dispatchers.Main.immediate。 一个令人困惑的现象…

I2C多点触控驱动开发详解

I2C多点触控驱动开发详解 1. 多点触控技术概述 1.1 触控技术发展历程 触控技术作为人机交互的重要方式&#xff0c;经历了从单点触控到多点触控的演进过程。早期的电阻式触控屏只能实现单点触控&#xff0c;限制了用户体验。随着电容式触控技术的发展&#xff0c;多点触控成为可…

UE5提升分辨率和帧率的方法

提问&#xff1a;分辨率大概理解就是是否模糊&#xff0c;帧率大概理解就是是否卡顿对吗 回答 没错&#xff0c;一句话总结&#xff1a; 分辨率主要影响“看起来糊不糊”&#xff1b; 帧率与帧时间稳定性主要影响“顺不顺”。 如何快速提升UE5的分辨率&#xff1f; 是的&…

小狼毫输入法中让数字键盘上的数字键不再选择候选词而是与原始输入一起直接上屏

使用搜狗输入法的双拼时&#xff0c;输入“womf”然后按下主键盘上的数字1&#xff0c;会选择排名第一的候选词上屏&#xff08;大概率是“我们&#xff09;&#xff0c;输入“womf”然后按下数字键盘上的数字1&#xff0c;不会选择候选词&#xff0c;而是将输入文本变成“womf…

【C++】类和对象(终章)

作者主页&#xff1a;lightqjx 本文专栏&#xff1a;C 目录 一、构造函数 1. 构造函数体赋值 2. 初始化列表 &#xff08;1&#xff09;基本概念 &#xff08;2&#xff09;使用特性 3. explicit关键字 二、static成员 1. 概念 2. 特性 3. 应用 三、友元 1. 友元函…

水果目标检测[2]:ALAD-YOLO:一种轻便、精确的苹果叶病检测仪

原文&#xff1a; 目录 摘要&#xff1a; ALAD-YOLO的改进&#xff1a; 1.轻量化主干网络&#xff1a; 2.改进的 Neck 网络&#xff1a; 3.改进的 SPP 模块&#xff1a; 4.注意力机制引入&#xff1a; 实验结果 数据&#xff1a; 1 数据采集 (Data Collection) 2 数…