C++ string的模拟实现
Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!!
一、string的框架搭建
1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需要一个文件来实现方法;




包一下头文件ok初步工作完成;
1.2明确string需要用到的成员变量以及成员函数
成员变量:
①必不可少的一个指向字符串的char*指针;(注意这里不能设置为const char*)
②字符串存储的有效数据size_t size;(有效数据指的时'\0'之前的数据)
③字符串存储的那块空间的容量size_t capacity;
④一个无符号最大值npos(用来判断);
成员函数:
①string的构造函数;包括用字符串构造、用string对象构造、用n个字符构造、用迭代器区间构造;实现常用的几个即可;
②string的析构函数;(因为涉及深拷贝所以要写)
③string的运算符重载函数;包括赋值运算符重载,比较小于、大于、等于、不等于运算重载函数,还有下标访问的运算符重载函数,流插入流提取运算符重载函数;
普通成员函数:
①开空间并初始化的 resize函数;
②开空间不初始化的reserve函数;
③插入单个字符的push_back函数;
④插入字符串的+=运算符重载函数;
⑤某个位置插入n个字符的insert函数;
⑥某个位置删除n个字符的erase函数;
⑦获取string有效数据的size()函数;
⑧获取string存储空间大小的capacity()函数;
⑨清理字符串的clear函数;
⑩从某个位置开始查找某个字符的find函数;
11、从某个位置开始截取长度为len的字符串;
12、获取_str的c_str函数;
还有一个需要用到的是迭代器;
1.3声明到头文件
由于模拟实现的string类的名字会跟std里的string冲突,所以需要namespace一个命名空间;
#pragma once
#include <iostream>
using namespace std;
//string的模拟实现
namespace ldc
{struct string{typedef char* iterator;typedef const char* const_iterator;public:string(const char* str);string(const string& s);string(size_t n, char c);string(const_iterator it1, const_iterator it2);~string();string& operator=(const string& s);bool operator>(const string& s)const;bool operator<(const string& s)const;bool operator==(const string& s)const;bool operator!=(const string& s)const;bool operator >= (const string & s)const;bool operator <=(const string& s)const;char operator[](size_t pos);const char operator[](size_t pos)const;void resize(size_t n, char c='\0');void reserve(size_t n);void push_back(char c);string& operator+=(const char* str);string& opeartor += (const string & s);string& ldc::string::operator+=(char ch);void insert(size_t pos, size_t n, char c);void erase(size_t pos, size_t n=npos);size_t size();size_t capacity();void clear();size_t find(char c, size_t pos = 0);size_t find(const char* str, size_t pos = 0);string& substr(size_t pos = 0, size_t len = npos);const char* c_str const();private:char* _str;size_t _size;size_t _capacity;const static size_t npos=-1;};ostream& operator<<(ostream& out, const string& s);//流提出插入重载istream& operator>>(istream& in, string& s);//在类外重载不存在this
}
这里注意:npos用来做判断,当调用erase在pos位置删除n个字符时,如果不传参n默认为npos(无符号最大值),表示从pos位置开始往后的全删除;(同理substr也一样);
二、开始实现
2.1构造函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "string3_20.h"
#include <string.h>
//string的方法实现
//构造函数
ldc::string::string(const char* str="")
{size_t len = strlen(str);_size = len;_capacity = _size;_str = new char[_capacity + 1];//这里+1因为要保留一个位置存放'\0',空串也有'\0'memcpy(_str, str, len + 1);
}
ldc::string::string(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _capacity + 1);
}
ldc::string::string(size_t n, char c)
{_size = n;_capacity = _size;_str = new char[_capacity + 1];for (size_t i = 0; i < n; i++){_str[i] = c;}_str[n] = '\0';
}
ldc::string::string(const_iterator it1, const_iterator it2)
{_size = it2 - it1;_capacity = _size;_str = new char[_capacity + 1];memcpy(_str, it1, _size);_str[_size] = '\0';
}
顺便把流插入重载和c_str也实现了(好测试)
const char* ldc::string::c_str()const
{return _str;
}
ostream& ldc::operator<<(ostream& out, const string& s)
{out <<s.c_str();return out;
}
测试:
#define _CRT_SECURE_NO_WARNINGS 1
//string的测试
#include "string3_20.h"
int main()
{ldc::string s1("hello world!!"); //字符串构造const char* ch = "welcome"; ldc::string s2(ch);cout << s1 << endl;cout << s2 << endl;char ch2[10] = { "yes!!!" };ldc::string s3(ch2, ch2 + 3);//区间构造cout << s3 << endl;ldc::string s4(s3);//拷贝构造cout<<s4<<endl;return 0;
}

ok没问题!接下来实现以下赋值重载跟析构;
2.2赋值重载与析构函数
ldc::string& ldc::string::operator=(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _capacity + 1);return *this;
}
ldc::string::~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
测试:
int main()
{ldc::string s1("hello");ldc::string s2("welcome");s1 = s2;cout << s1 << endl;return 0;
}

没问题,接下来实现普通成员函数;
2.3普通成员函数的实现
//比较、下标访问运算符重载
bool ldc::string::operator>(const string& s)const
{//比较有很多种方法,可以用operator[]比也可以获取str后再比,这里用最简洁的方法//库里的memcmp内存比较函数int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//先比短return ret == 0 ? _size > s._size:ret > 0;//再比长,如果相等ret为正数表示str1>str2
}
bool ldc::string::operator<=(const string& s)const
{return !(*this > s);
}
bool ldc::string::operator<(const string& s)const
{return !(*this >= s);
}
bool ldc::string::operator>=(const string& s)const
{return (*this > s) || (*this == s);
}
bool ldc::string::operator==(const string& s)const
{return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool ldc::string::operator!=(const string& s)const
{return !(*this == s);
}char ldc::string::operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
const char ldc::string::operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}
测试:
void test1()
{ldc::string s1("hello");ldc::string s2("helloxxx");ldc::string s3("welcome");ldc::string s4("welcome");cout << (s1 > s2) << " ";cout << (s1 >= s2) << " ";cout << (s1 < s2) << " ";cout << (s1 <= s2) << " ";cout << (s3 == s4) << " ";cout << (s3!= s2) << " ";cout << s1[2] << " ";s1[0]='Y';cout << s1 << " ";//const ldc::string s5("beutiful!");//s5[0] = 'B';
}
int main()
{test1();return 0;
}

OK没问题接着实现扩容:
2.4扩容实现
//只开空间不初始化
void ldc::string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];//假设开100个空间,需要存放100个数据,第101个位置需存放结束标识符'\0'memcpy(tmp, _str, _size + 1);//连'\0'一起拷贝,不然开完空间后的数据没有结束标志符delete[] _str;//释放旧空间_str = tmp;//指向新空间_capacity = n;//注意只需要修改_capacity,_size有效数据始终没变}
}//开空间并初始化
void ldc::string::resize(size_t n, char c = '\0')
{if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i=_size;i<n;i++){_str[i] = c;}_str[n] = '\0';_size = n;}
}
测试:
void test2()
{ldc::string s1("hello");s1.resize(20, 'x');cout << s1 << endl;s1.resize(3);cout << s1 << endl;
}
int main()
{//test1();test2();return 0;
}

ok没问题下一步,reserve后面测;
2.5增删查改实现
void ldc::string::push_back(char c)
{if (_size == _capacity){size_t n = _capacity == 0 ? 4 : _capacity * 2;//二倍扩容reserve(n);}_str[_size] = c;++_size;_str[_size] = '\0';
}
ldc::string& ldc::string::operator+=(const char* str)
{size_t len = strlen(str);reserve(_size + len);//复用reserve开空间memcpy(_str + _size, str, len + 1);//'\0'一起拷贝过去_size += len;return *this;
}
ldc::string& ldc::string::operator+=(char ch)
{push_back(ch);return *this;
}
ldc::string&ldc::string::operator+= (const string & s)
{reserve(_size + s._size);memcpy(_str + _size, s._str, s._size + 1);_size += s._size;return *this;
}
void ldc::string::insert(size_t pos, size_t n, char c)
{assert(pos <= _size);reserve(_size + n);if (pos==_size)//尾插{while (n--){push_back(c);}}else{char* begin = _str + _size;char* newbegin = _str + pos;char* end = _str + _size + n;while (begin>=newbegin){*end = *begin;--end;--begin;}int i = n;while (i--){_str[pos] = c;++pos;}_size += n;}}
void ldc::string::erase(size_t pos, size_t n)
{assert(pos < _size);if (n == npos || pos + n > _size){_str[pos] = '\0';}else{size_t end = _size;size_t begin = pos + n;while (begin != end){_str[pos] = _str[begin];++pos; ++begin;}}_size -= n;_str[_size] = '\0';
}
size_t ldc::string::find(char c, size_t pos)
{assert(pos < _size);while (pos != _size){if (_str[pos] == c){return pos;}++pos;}return npos;
}
size_t ldc::string::find(const char* str, size_t pos)
{//直接用strstr函数char* ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}elsereturn npos;
}
测试:
void test3()
{ldc::string s1("hello world!!");s1.insert(5, 3, 'X');cout << s1 << endl;s1.insert(0, 3, 'Y');cout << s1 << endl;s1.insert(19, 2, 'Z');cout << s1 << endl;s1.erase(0, 3);cout << s1 << endl;s1.erase(5, 4);cout << s1 << endl;s1.erase(10, -1);cout << s1 << endl;cout << s1.find('h')<<endl;cout << s1.find("world") << endl;cout << s1.find('l', 3) << endl;
}

没啥问题,接着把剩下的都实现了;
三、剩余完善
3.1substr实现
ldc::string ldc::string::substr(size_t pos, size_t len)
{assert(pos < _size);size_t n = len;if (n == npos || pos + n > _size){n = _size - pos;}string temp;temp.reserve(n);for (size_t i = 0; i < n; i++){temp += _str[pos + i];}return temp;
}
3.2其他实现
size_t ldc::string::size()const
{return _size;
}
size_t ldc::string::capacity()const
{return _capacity;
}
void ldc::string::clear()
{_str[0] = '0';_size = 0;
}
istream& ldc::operator>>(istream& in, string& s)
{s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}//in >> ch;char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}
3.3测试
void test4()
{ldc::string s1("hello");ldc::string s2(s1.substr(0, 2));cout << s1 << endl;cout << s2 << endl;cout <<"开空间前:" << s2.capacity() <<" ," << s2.size() << endl;s2.reserve(40);cout <<"开空间后:"<< s2.size() << ", " << s2.capacity() << endl;cin>>s2;cout << s2<<endl;cout << "流插入后:" << s2.size() << " " << s2.capacity() << endl;
}

OK!!到这里string的基本功能已经实现了!!如果您觉得有所收获,记得点赞收藏+关注哦!!谢谢!!!
咱下期见!!!
相关文章:
C++ string的模拟实现
Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!! 一、string的框架搭建 1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需…...
使用LangChain实现基于LLM和RAG的PDF问答系统
目录 前言一.大语言模型(LLM)1. 什么是LLM?2. LLM 的能力与特点 二、增强检索生成(RAG)三. 什么是 LangChain?1. LangChain 的核心功能2. LangChain 的优势3. LangChain 的应用场景4. 总结 四.使用 LangChain 实现基于 PDF 的问答系统 前言 本文将介绍 …...
图像滤波中常用滤波器的相位响应——不是只有零相位滤波器
实偶函数滤波器 当滤波器是实偶函数时,其滤波结果的相位在通带内为 0 或 π \pi π,正频率和负频率成分的相位相同。这种相位特性使得实偶函数滤波器在低通滤波、平滑处理等需要保持信号相位不失真的应用中非常有用。 实偶函数特性: 滤波器…...
学习CSS滤镜属性 `filter: invert()`
一、核心机制 数学原理 invert(1) 对每个像素的RGB通道执行 颜色反相计算: 新通道值 255 - 原通道值 例如 rgb(255,0,0)(纯红)会转换为 rgb(0,255,255)(青色)。 透明度处理 该滤镜会保留元素的Alpha通道(…...
C++实现rabbitmq生产者消费者
RabbitMQ是一个开源的消息队列系统,它实现了高级消息队列协议(AMQP), 特点 可靠性:通过持久化、镜像队列等机制保证消息不丢失,确保消息可靠传递。灵活的路由:提供多种路由方式,如…...
在VMware上部署【Ubuntu】
镜像下载 国内各镜像站点均可下载Ubuntu镜像,下面例举清华网站 清华镜像站点:清华大学开源软件镜像站 | Tsinghua Open Source Mirror 具体下载步骤如下: 创建虚拟机 准备:在其他空间大的盘中创建存储虚拟机的目录,…...
【Pandas】pandas Series plot.barh
Pandas2.2 Series Plotting 方法描述Series.plot([kind, ax, figsize, …])用于绘制 Series 对象的数据可视化图表Series.plot.area([x, y, stacked])用于绘制堆叠面积图(Stacked Area Plot)Series.plot.bar([x, y])用于绘制垂直条形图(Ver…...
检索增强生成(2)本地PDF 本地嵌入模型
from langchain_community.document_loaders import PyPDFLoader from pathlib import Pathdef load_local_pdf(file_path):if not Path(file_path).exists():raise FileNotFoundError(f"文件 {file_path} 不存在!")loader PyPDFLoader(file_path)try:do…...
又双叒叕Scrapy爬虫相关的面试题及详细解答
Scrapy是Python开发的一个快速、高层次的网络爬虫框架,专注于高效抓取网页并提取结构化数据。其核心设计基于异步处理机制,适合大规模数据采集任务。 文章目录 基础概念1. Scrapy框架的核心组件有哪些?架构与流程2. 描述Scrapy的工作流程核心组件详解3. 如何自定义Item Pipe…...
【QA】装饰模式在Qt中有哪些运用?
在Qt框架中,装饰模式(Decorator Pattern)主要通过继承或组合的方式实现,常见于IO设备扩展和图形渲染增强场景。以下是Qt原生实现的装饰模式典型案例: 一、QIODevice装饰体系(继承方式) 场景 …...
【保姆级】阿里云codeup配置Git的CI/CD步骤
以下是通过阿里云CodeUp的Git仓库进行CI/CD配置的详细步骤,涵盖前端(Vue 3)和后端(Spring Boot)项目的自动化打包,并将前端打包结果嵌入到Nginx的Docker镜像中,以及将后端打包的JAR文件拷贝至Do…...
使用STM32CubeMX+DMA+空闲中断实现串口接收和发送数据(STM32G070CBT6)
1.STM32CubeMX配置 (1)配置SYS (2)配置RCC (3)配置串口,此处我用的是串口4,其他串口也是一样的 (4)配置DMA,将串口4的TX和RX添加到DMA中 &#…...
【视觉提示学习】3.21论文随想
. . Frontiers of Information Technology & Electronic Engineering. 2024, 25(1): 42-63 https://doi.org/10.1631/FITEE.2300389 中文综述,根据里面的架构,把视觉提示学习分成两类,一类是单模态提示学习(以vit为代表&…...
(一)丶Windows安装RabbitMQ可能会遇到的问题
一丶可能会忘了配置ERLang的环境变量 二丶执行命令时报错 第一步 rabbitmq-plugins enable rabbitmq_management 第二部 rabbitmqctl status 三丶修改.erlang.cookie 文件 1.找到C盘目下的.erlang.cookie文件 C:\Users\admin\.erlang.cookie C:\Windows\System32\config\sys…...
Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能
法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 ,该模型凭借240亿参数的轻量级设计,在多项基准测试中表现优异,甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…...
如何在IPhone 16Pro上运行python文件?
在 iPhone 16 Pro 上运行 Python 文件需要借助第三方工具或远程服务,以下是具体实现方法和步骤: 一、本地运行方案(无需越狱) 使用 Python 编程类 App 以下应用可在 App Store 下载,支持直接在 iPhone 上编写并运行 …...
springboot整合mybatis-plus【详细版】
目录 一,简介 1. 什么是mybatis-plus2.mybatis-plus特点 二,搭建基本环境 1. 导入基本依赖:2. 编写配置文件3. 创建实体类4. 编写controller层5. 编写service接口6. 编写service层7. 编写mapper层 三,基本知识介绍 1. 基本注解 T…...
视频剪辑行业的现状与进阶之路:一个双视角分析
视频剪辑行业的现状与进阶之路:一个双视角分析 一、现状解析 商业角度分析 成本控制 培训需要投入时间和人力成本 快节奏的市场环境要求快速产出 人员流动性大,培训投入可能无法获得长期回报 市场需求 大量内容需要快速产出 标准化的剪辑模板更容易管理 …...
k近邻图(knn-graph)和局部线性嵌入图(LLE-graph)的相似性和区别
K 近邻图(KNN - graph)和局部线性嵌入图(LLE - graph)是用于构建数据点之间关系图的两种方法。 1. k近邻图(knn-graph) 核心思想:k近邻图通过计算样本之间的距离来构建图。具体来说,…...
Qt之MVC架构MVD
什么是MVC架构: MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controll…...
使用 Apktool 反编译、修改和重新打包 APK
使用 Apktool 反编译、修改和重新打包 APK 在 Android 逆向工程和应用修改过程中,apktool 是一个强大的工具,它允许我们解包 APK 文件、修改资源文件或代码,并重新打包成可安装的 APK 文件。本文将介绍如何使用 apktool 进行 APK 反编译、修…...
深度解析学术论文成果评估(Artifact Evaluation):从历史到现状
深度解析学术论文成果评估(Artifact Evaluation):从历史到现状 引言 在计算机科学和工程领域的学术研究中,可重复性和可验证性越来越受到重视。随着实验性研究的复杂性不断增加,确保研究成果可以被其他研究者验证和构建变得尤为重要。这一需…...
二分查找上下界问题的思考
背景 最近在做力扣hot100中的二分查找题目时,发现很多题目都用到了二分查找的变种问题,即二分查找上下界问题,例如以下题目: 35. 搜索插入位置 74. 搜索二维矩阵 34. 在排序数组中查找元素的第一个和最后一个位置 它们不同于查找…...
关于FastAPI框架的面试题及答案解析
FastAPl是一个现代、快速(高性能)的Web框架,用于构建API,基于Python3.7+的类型提示功能。它由Python开发者SebastianRamirez创建,并且使用了Starlette作为其核心组件以及Pydantic进行数据验证。 文章目录 基础篇1. FastAPI的核心优势是什么?2. 如何定义一个GET请求路由?…...
Ubuntu检查并启用 Nginx 的stream模块或重新安装支持stream模块的Nginx
stream 模块允许 Nginx 处理 TCP 和 UDP 流量,常用于负载均衡和端口转发等场景。本文将详细介绍如何检查 Nginx 是否支持 stream 模块,以及在需要时如何启用该模块。 1. 检查 Nginx 是否支持 stream 模块 首先,需要确认当前安装的 Nginx 是…...
HashMap添加元素的流程图
文章目录 JDK7 vs JDK8 的 HashMap 结构变化Java8 中哈希表的红黑树优化机制HashMap 添加元素的完整流程解析1. 计算 key 的哈希值并确定索引2. 检查该索引位置是否已有元素3. 处理哈希冲突4. 判断当前存储结构(链表还是红黑树)5. 判断链表长度是否超过 …...
(八)Set 的使用
Set 的使用 Set 的特点 主要功能:去除重复内容。特性:无序且不支持重复的集合,不能通过索引访问元素。 示例代码 void main() {// 创建一个包含重复元素的列表List<String> fruits [香蕉, 苹果, 西瓜, 香蕉, 苹果, 香蕉, 苹果];//…...
Spring Boot 集成 Kafka 消息发送方案
一、引言 在 Spring Boot 项目中,Kafka 是常用的消息队列,可实现高效的消息传递。本文介绍三种在 Spring Boot 中使用 Kafka 发送消息的方式,分析各自优缺点,并给出对应的 pom.xml 依赖。 二、依赖引入 在 pom.xml 中添加以下依赖: <dependencies><!-- Sprin…...
面向医药仓储场景下的药品分拣控制策略方法 研究(大纲)
面向医药仓储场景下的药品分拣控制策略方法研究 基于多机器人协同与智能调度的分拣系统设计 第一章 绪论 1.1 研究背景与意义 医药仓储自动化需求: 人工分拣效率低、出错率高(如药品批次混淆、过期风险)温控药品(如疫苗、生物制…...
AI大模型介绍
大模型介绍 大模型是指具有大规模参数和复杂计算结构的机器学习模型,通常由深度神经网络构建而成,拥有数十亿甚至数千亿个参数 开发大模型不是从0开始,是建立在已有的大模型基座模型上做开发,构建企业知识库(向量数据库…...
