C++ Learning string类模拟实现

article/2025/6/6 4:42:14

string类模拟实现

std::string 类作为 C++ 标准库中非常重要的一个类型,它封装了字符串的动态分配、内存管理以及其他字符串操作。

基本构思与设计

一个简化版的 string 类需要满足以下基本功能:

  • 存储一个字符数组(char*)。
  • 记录字符串的长度和容量。
  • 实现一些常见的字符串操作:如拷贝构造、赋值、拼接、访问字符等。
  • 动态扩展数组以支持变长字符串。

基础成员变量和构造函数

首先,我们定义一个 String 类的基本结构。为了模拟 std::string,我们需要存储字符数组、字符串的大小以及容量。

class String
{
public:// ...private:char* data;size_t size;size_t capacity;
};

其中data是字符数组,size表示已存储字符个数,capacity表示能存储字符个数(不包括"\0"

// 默认构造函数
String():data(new char[1])
{data[0] = '\0';size = capacity = 0;
}// 构造函数:根据 C 字符串初始化
String(const char* str)
{size = strlen(str);capacity = size;data = new char[capacity + 1]; // 多一个空间用于存放'\0'strcpy(data, str);
}

两个构造函数还可以合二为一采用缺省值写法。

String::String(const char* str = "")
{size = strlen(str);capacity = size;data = new char[capacity + 1];strcpy(data, str);
}

特别注意:此处的缺省值只能给str = ""表示一个空串。若给其他值会导致在构造空串时不合理。

拷贝构造函数和赋值

// 拷贝构造函数
String(const String& rhs)
{size = rhs.size;capacity = rhs.capacity;data = new char[capacity + 1];strcpy(data, rhs.data);
}// 赋值操作符重载
String& operator=(const String& rhs)
{// 检查自赋值    if (this != &rhs){delete[] data; // 释放原有的内存资源size = rhs.size;capacity = rhs.capacity;data = new char[capacity + 1];strcpy(data, rhs.data);}return *this;
}

特别注意:赋值操作符重载时不要忘记释放原有的内存资源

reserve函数

// 重新分配内存
void String::reserve(size_t new_capacity)
{// 如果新的容量小于当前容量,不做任何事情if (new_capacity > capacity) {char* new_data = new char[new_capacity + 1];strcpy(new_data, data);delete[] data;// 释放原有的内存资源data = new_data;capacity = new_capacity;}
}

字符串拼接相关函数

// 追加字符
void push_back(char ch)
{if (size == capacity){// 重新分配内存// 先判断capacity是否为0,如果为0,新的容量为2,否则为原来的2倍size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;reserve(new_capacity);}data[size++] = ch;data[size] = '\0'; // 添加字符串结束符
}// 追加字符串
void append(const char* str)
{size_t new_size = size + strlen(str);if (new_size > capacity){// 重新分配内存size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;// 扩容到足够容纳新字符串的大小,以2倍数扩容while (new_capacity < new_size){new_capacity *= 2;}reserve(new_capacity);}strcat(data, str);size = new_size;
}
重载+=运算符
// 重载+=运算符
String& String::operator+=(char ch)
{push_back(ch);return *this;
}
String& String::operator+=(const char* str)
{append(str);return *this;
}

直接复用上面的代码

insert函数
String& insert(size_t pos, char ch)
{assert(pos <= size);if (size == capacity){// 重新分配内存size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;reserve(new_capacity);}// 将pos位置及其后面的字符向后移动一个位置for (size_t i = size; i > pos; --i){data[i] = data[i - 1];}data[pos] = ch;++size;return *this;
}String& insert(size_t pos, const char* str)
{assert(pos <= size);size_t len = strlen(str);size_t new_size = size + len;if (new_size > capacity){// 重新分配内存size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;// 扩容到足够容纳新字符串的大小,以2倍数扩容while (new_capacity < new_size){new_capacity *= 2;}reserve(new_capacity);}// 将pos位置及其后面的字符向后移动len个位置for (size_t i = size; i >= pos; --i){data[i + len] = data[i];}// 将str插入到pos位置for (size_t i = 0; i < len; ++i){data[pos + i] = str[i];}size = new_size;return *this;
}

此处需要注意挪动数据的方式。

erase函数

// 删除函数
String& erase(size_t pos, size_t len = npos)
{assert(pos < size);// 如果len超过pos到字符串末尾的长度,只删除pos到字符串末尾的长度if (len > size - pos) {data[pos] = '\0';size = pos;}else {// 将pos+len位置及其后面的字符向前移动len个位置size_t i = pos + len;while (i <= size){data[pos++] = data[i++];}size -= len;}return *this;
}

其中npos是一个常量

const static size_t npos = -1;

resize函数

// 调整字符串大小
void resize(size_t new_size, char ch = '\0')
{if (new_size > size){if (new_size > capacity){// 重新分配内存reserve(new_size);}// 将ch添加到字符串末尾for (size_t i = size; i < new_size; ++i){data[i] = ch;}}data[new_size] = '\0'; // 添加字符串结束符size = new_size;
}

查找函数

size_t find(char ch, size_t pos) const
{for (size_t i = pos; i < size; ++i) {if (data[i] == ch) {return i;}}return npos;
}size_t find(const char* str, size_t pos) const
{char* p = strstr(data + pos, str);return p ? p - data : npos;
}

输入输出

istream& operator>>(istream& is, String& str)
{char ch;ch = is.get();while (ch != ' ' && ch != '\n') {str.push_back(ch);ch = is.get();// 读取下一个字符,可以读取空格}return is;
}istream& getline(istream& is, String& str)
{char ch;ch = is.get();while (ch != '\n') {str.push_back(ch);ch = is.get();// 读取下一个字符,可以读取空格}return is;
}ostream& operator<<(ostream& os, const String& str)
{os << str.c_str();return os;
}

比较运算符重载

bool operator==(const String& lhs, const String& rhs)
{int ret = strcmp(lhs.c_str(), rhs.c_str());return ret == 0;
}bool operator!=(const String& lhs, const String& rhs)
{return !(lhs == rhs);
}bool operator<(const String& lhs, const String& rhs)
{int ret = strcmp(lhs.c_str(), rhs.c_str());return ret < 0;
}bool operator>(const String& lhs, const String& rhs)
{int ret = strcmp(lhs.c_str(), rhs.c_str());return ret > 0;
}bool operator<=(const String& lhs, const String& rhs)
{return !(lhs > rhs);
}bool operator>=(const String& lhs, const String& rhs)
{return !(lhs < rhs);
}

重载[]运算符

char& operator[](size_t i)
{return data[i];
}const char& operator[](size_t i) const
{return data[i];
}

迭代器

iterator begin()
{return data;
}const_iterator begin() const
{return data;
}iterator end()
{return data + size;
}const_iterator end() const
{return data + size;
}

其中iterator来自typedef

typedef char* iterator;
typedef const char* const_iterator;

大小相关

size_t Size() const
{return size;
}size_t length() const
{return size;
}size_t Capacity() const
{return capacity;
}

完整代码

GitHub链接


http://www.hkcw.cn/article/jLdrZjfsbN.shtml

相关文章

第11节 Node.js 模块系统

为了让Node.js的文件可以相互调用&#xff0c;Node.js提供了一个简单的模块系统。 模块是Node.js 应用程序的基本组成部分&#xff0c;文件和模块是一一对应的。换言之&#xff0c;一个 Node.js 文件就是一个模块&#xff0c;这个文件可能是JavaScript 代码、JSON 或者编译过的…

『uniapp』把接口的内容下载为txt本地保存 / 读取本地保存的txt文件内容(详细图文注释)

目录 预览效果思路分析downloadTxt 方法readTxt 方法 完整代码总结 欢迎关注 『uniapp』 专栏&#xff0c;持续更新中 欢迎关注 『uniapp』 专栏&#xff0c;持续更新中 预览效果 思路分析 downloadTxt 方法 该方法主要完成两个任务&#xff1a; 下载 txt 文件&#xff1a;通…

XCTF-web-ics-05

看一下有什么 只有/index.php 模糊测试得到一个page ┌──(kali㉿kali)-[~] └─$ ffuf -u "http://223.112.5.141:52073/index.php?FUZZFUZZ" -w /usr/share/wordlists/rockyou.txt -fc 403 -c -fs 2305 -s page尝试用php伪协议读取源码?pagephp://filter/readc…

Redis线程模型

前面的文章介绍了Redis的底层数据结构&#xff0c;这篇文章来介绍一下Redis的线程模型。 Redis为什么选择单线程&#xff1f; 官方的回答是这样的&#xff0c;对于Redis来说&#xff0c;CPU通常不会成为瓶颈&#xff0c;因为大多数的请求不会是CPU密集型的&#xff0c;而是IO密…

工厂方法模式深度解析:从原理到应用实战

作者简介 我是摘星&#xff0c;一名全栈开发者&#xff0c;专注 Java后端开发、AI工程化 与 云计算架构 领域&#xff0c;擅长Python技术栈。热衷于探索前沿技术&#xff0c;包括大模型应用、云原生解决方案及自动化工具开发。日常深耕技术实践&#xff0c;乐于分享实战经验与…

STM32入门教程——按键控制LED光敏传感器控制蜂鸣器

前言 本教材基于B站江协科技课程整理&#xff0c;适合有C语言基础、刚接触STM32的新手。它梳理了STM32核心知识点&#xff0c;帮助大家把C语言知识应用到STM32开发中&#xff0c;更高效地开启STM32学习之旅。 目录 前言 一、硬件接线与模块化编程概述 二、LED 驱动模块开发…

K8s基础一

Kubernetes 架构 Kubernetes 背后的架构概念。 Kubernetes 集群由一个控制平面和一组用于运行容器化应用的工作机器组成&#xff0c; 这些工作机器称作节点&#xff08;Node&#xff09;。每个集群至少需要一个工作节点来运行 Pod。 工作节点托管着组成应用负载的 Pod。控制平…

Spring @Value注解的依赖注入实现原理

Spring Value注解的依赖注入实现原理 一&#xff0c;什么是Value注解的依赖注入二&#xff0c;实现原理三&#xff0c;代码实现1. 定义 Value 注解2. 实现 InstantiationAwareBeanPostProcessor3. 实现 AutowiredAnnotationBeanPostProcessor4. 占位符解析逻辑5. 定义 StringVa…

Oracle、PostgreSQL 与 MySQL 数据库对比分析与实践指南

一、三大数据库基础认知 Oracle数据库 基本概况 ✔ 厂商&#xff1a;Oracle Corporation ✔ 许可证&#xff1a;商业授权&#xff08;含Oracle XE免费版本&#xff09; ✔ 典型用户&#xff1a;大型银行、政府机构、电信运营商 核心特性 -- 示例&#xff1a;Oracle PL/SQL存…

protobuf arena实现概述

Arena是Protobuf的C特有特性&#xff0c;旨在优化内存分配效率&#xff0c;减少频繁的堆内存申请与释放。其核心机制如下&#xff1a; 预分配内存&#xff1a;Arena预先分配一大块连续内存&#xff08;称为Block&#xff09;&#xff0c;对象创建时直接从该内存块中分配&#x…

深入浅出图神经网络:从核心概念到实战落地

文章目录 1 引言1.1 发展脉络与现状1.2 面临挑战1.3 本文目标 2 图结构数据基础2.1 关键元素2.2 数学定义与常用符号2.3 图的常见类型2.4 为什么这些定义重要&#xff1f; 3 GNN 核心思想&#xff1a;消息传递机制3.1 消息函数 M E S S A G E ( k ) \mathrm{MESSAGE}^{(k)} ME…

6级阅读学习

先找连接词&#xff0c;and什么的 再找that什么的 最后找介词短语

当 AI 超越人类:从技术突破到文明拐点的 2025-2030 年全景展望

引言:当科幻照进现实的十年 2025 年的某个清晨,当你对着智能音箱说出 “帮我订一份早餐” 时,或许不会想到,这个简单指令背后的技术演进,正悄然推动人类文明走向一个前所未有的拐点。从弱人工智能(ANI)到强人工智能(AGI)的跃迁,不再是科幻小说的专属设定,而是现实世…

安全-JAVA开发-第一天

目标&#xff1a; 安装环境 了解基础架构 了解代码执行顺序 与数据库进行连接 准备&#xff1a; 安装 下载IDEA并下载tomcat&#xff08;后续出教程&#xff09; 之后新建项目 注意点如下 1.应用程序服务器选择Web开发 2.新建Tomcat的服务器配置文件 并使用 Hello…

Spring @Autowired自动装配的实现机制

Spring Autowired自动装配的实现机制 Autowired 注解实现原理详解一、Autowired 注解定义二、Qualifier 注解辅助指定 Bean 名称三、BeanFactory&#xff1a;按类型获取 Bean四、注入逻辑实现五、小结 源码见&#xff1a;mini-spring Autowired 注解实现原理详解 Autowired 的…

【AI News | 20250603】每日AI进展

AI Repos 1、dgm 是一个创新的自改进系统&#xff0c;通过迭代修改自身代码并利用编码基准验证每次更改&#xff0c;实现开放式进化。该系统旨在提升 AI 代理的代码修改能力。DGM 支持 OpenAI 和 Anthropic API&#xff0c;依赖 Docker 环境&#xff0c;并集成了 SWE-bench 和…

Rust 学习笔记:Cargo 工作区

Rust 学习笔记&#xff1a;Cargo 工作区 Rust 学习笔记&#xff1a;Cargo 工作区创建工作区在工作区中创建第二个包依赖于工作区中的外部包向工作区添加测试将工作区中的 crate 发布到 crates.io添加 add_two crate 到工作区总结 Rust 学习笔记&#xff1a;Cargo 工作区 随着项…

操作系统 第 39 章 插叙:文件和目录

两项关键操作系统技术的发展&#xff1a;进程&#xff0c;虚拟化的 CPU&#xff1b;地址空间&#xff0c;虚拟化的内存。 这一部分加上虚拟化拼图中最关键的一块&#xff1a;持久存储。永久存储设备永久地&#xff08;或至少长时间地&#xff09;存储信息&#xff0c;如传统硬盘…

楼宇自控系统联动暖通空调:解密建筑环境舒适度提升路径

走进现代建筑&#xff0c;无论是办公场所、商业中心&#xff0c;还是医院、酒店&#xff0c;人们对环境舒适度的要求越来越高。暖通空调作为调节建筑室内环境的关键设备&#xff0c;其运行效果直接影响着人们的体验。然而&#xff0c;传统暖通空调独立运行、调控不灵活等问题&a…

Freemarker快速入门

Freemarker概述 FreeMarker 是一款 模板引擎&#xff1a; 即一种基于模板和要改变的数据&#xff0c; 并用来生成输出文本(HTML网页&#xff0c;电子邮件&#xff0c;配置文件&#xff0c;源代码等)的通用工具。 它不是面向最终用户的&#xff0c;而是一个Java类库&#xff0c…