【C++初阶】string模拟实现

请添加图片描述

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌟🌟 追风赶月莫停留 🌟🌟
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
🌟🌟 平芜尽处是春山🌟🌟
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅

🍋string模拟实现

  • 🍑构造函数
  • 🍑析构函数
  • 🍑重载
    • 🍍方括号引用
    • 🍍+=操作
    • 🍍=操作
  • 🍑迭代器
  • 🍑增删查改
    • 🍍增
    • 🍍找
    • 🍍流提取和流插入

🍑构造函数

//有参构造
string(char* str)
{size_t _size = strlen(str);_capacity = _size;str = new char[_capacity+1];strcpy(_str, str);
}
//无参构造
string()
{_str = new char[1];_size = 0;_capacity = 0;_str[0] = '\0';
}

至于无参构造中,为了防止解引用发生错误,所以特有开了一个空间,并赋值为斜杠0。

上面代码就是普通的构造函数的写法,无参和有参构造,这里我们并不采用初始化列表的方式进行初始化。

但实际我们并不会这么写,会直接写一个全缺省:

string (const char* str = "\0")
{size_t _size = strlen(str);_capacity = _size;_str = new char[_capacity+1];strcpy(_str, str);
}

上面中初始化时并不能直接赋值给’\0’,str是字符串类型而’\0’是字符,两者类型不一样,所以应赋值"\0"。当然也可以不用赋值,直接空字符串也是,因为常量字符串中自带斜杠0。

🍑析构函数

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

析构函数就没有什么特别需要注意的了,和常规写法差不多

🍑重载

🍍方括号引用

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

引用在这里的作用是减少拷贝和修改返回值,如果单纯返回该里面的元素,就不会用引用了。

🍍+=操作

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

operator+=()函数操作就会简单就能实现,直接分别调用push_back()和append()函数即可,当然也可以自己在里面实现,不过实现的过程都差不多。

还有一个点需要大家注意,就是运算符重载需要有返回值。

🍍=操作

string& operator=(cconst string& s)
{if(_str != &s){char* tem = new char[s._capacity+1];strcpy(tem, s._str);delete[] _str;_str = tem;_size = s._size;_capacity = s._capacity;}return *this;
}

该函数就是重载等号,如:

string s1("abcdef");
string s2 = s1;

该函数的作用就是string类同等类型相互赋值时,

🍑迭代器

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

string类中迭代器的实现就很简单了。

不同容器的迭代器方式不一样,大家以后可以在学习其它容器的迭代器实现的时候可以进行比较着学习

🍑增删查改

void reserve(size_t n)
{if (n > _capacity){char* cur = new char[n+1];strcpy(cur, _str);delete[] _str;_str = cur;_capacity = n;}
}

这是一个扩容函数,在push_back或insert时,空间都有可能满,所以会先进行扩容。

🍍增

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

push_back()函数是在原本string中最后面的位置添加一个字符。

void append(const char* str)
{size_t len = strlen(str);if (_size+len > _capacity){reserve(_size+len);}strcpy(_str+_size, str);_size += len;
}

append()函数和push_back()函数还是有点大径相同,只不过append()是增加一个字符串在原string类后面,不过有一些细节还是需要注意。

void insert(size_t pos, char str)
{assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size ;while (end >= pos){_str[end + 1] = str[end];end--;}_str[pos] = str;_size++;
}

insert()函数是一个可以在容器内插入字符的函数,在进行插入的时候都要对容器内的空间进行检查,防止空间满。在这里还要注意一点,我们是利用了end来当做循环条件,而end的类型是size_t ,而size_t是无符号整型。

而当pos为0时,这个时候循环结束条件就是end=-1,但end是size_t 类型,所以当end=-1时,实际上并不会得到你所期望的负数结果。由于size_t是一个无符号整形,所以它会将-1解释为一个非常大的正数。

在这里插入图片描述
所以这种情况下,程序就会陷入死循环。

void insert(size_t pos, char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size;while (end >= pos){_str[end + len] = _str[end];end--;}strncpy(_str+pos, str, len);_size += len;
}

该insert()函数和上面的insert()函数功能差不多,但是这里insert()是插入一段字符,而上面的insert()函数是插入一个字符。

这里最后不能用strcpy(),因为strcpy()会把斜杠0一起复制过去,这样就会覆盖原有的一个值。

🍍找

size_t Find(char ch, size_t pos = 0)
{for(size_t i = pos; i < _str.size(); i++){if (_str[i] == ch){return i;}}return npos;
}

Find()函数是查找某个元素,并返回下标值,npos是全局静态变量,它值为-1, 在这里我们给了pos默认值,默认是从0开始找,也可以指定从那个数开始找。

size_t Find(const char* str, size_t pos = 0)
{const char* ptr = strstr(_str+pos,  str);if (ptr == nullptr){return npos;}else{return ptr - _str;}
}

该函数是查找一段字符串的函数,并返回该字符串的起始位置,npos是全局静态变量,值为-1;

string substr(size_t pos, size_t len = npos)
{assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len > _size){end = _size;}string str;for (size_t i = pos; i < end; i++){str += _str[i];}return str;
} 

substr()函数从主容器中取出指定范围的数据,其中npos是在上面已经提到过了,而在这len也是size_t类型,上面说过size_t是无符号整型,一旦len为负数,系统就会理解len是一个很大的值,也就相当于要取出的数大于pos后面的数据长度了,所以只是把后面的数全部取出来,和pos+len的情况是一样,所以在这里就吧它俩当成一种情况

使用substr()函数时要注意有没有深拷贝构造函数,程序会报错,需要提前写构造函数:

string(const string& s)
{_str = new char[s._capacity+1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity; 
}

🍍流提取和流插入

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

不仅仅可以使用范围for,也可以使用迭代器或者原生指针输出。

该函数就是模拟流插入。string类的流提取函数写法还是很简单,另外这里也不需要写成友元形式,上面我们模拟实现了很多函数,都可以帮助我们完成,不过也有一些类的流提取需要加友元形式,才能正常运行。

void clear()
{_size = 0;_str[0] = '\0';
}istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();while(ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in
}

上述是流提取函数,get()是C++中读取空格的操作符,相当于C语言中getchar()是一个作用。

关于clear()函数,因为我们在这里是 s += ch,是+=操作,所以数据并不会覆盖,所以我们需要先清理掉原有数据,再进行流插入。

这里关于流提取还有一个小问题,当我们输入的字符过多的时候,如果按照原来的扩容方法就会造成空间浪费,有一个改进的写法:

void clear()
{_size = 0;_str[0] = '\0';
}istream& operator>>(istream& in, string& s)
{s.clear();char open[128] = {0};char ch = in.get();while(ch != ' ' && ch != '\n'){open[i++] = ch;if (i == 127){open[i] = '\0';s += open;i = 0;}ch = in.get();}if (i > 0){open[i] = '\0';s += ch;}return in;
}

改进过的流提取就节省了很多空间,这里是利用了数组open,来临时存储数据,数组满了,再直接利用操作符"+="操作即可。

关于本章知识点如果有不足或者遗漏,欢迎大家指正,谢谢!!!

请添加图片描述
请添加图片描述

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

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

相关文章

【精读Yamamoto】方向性连接如何丰富神经网络的功能复杂度 | 体外神经元培养实验 | 脉冲神经元模型(SNN) | 状态转移模型

探索大脑的微观世界&#xff1a;方向性连接如何丰富神经网络的功能复杂度 在神经科学领域&#xff0c;理解大脑如何通过其复杂的网络结构实现高级功能一直是一个核心议题。最近&#xff0c;一项由Nobuaki Monma和Hideaki Yamamoto博士领导的研究为我们提供了新的视角&#xff…

cuttag学习笔记

由于课题可能用上cut&tag这个技术&#xff0c;遂跟教程学习一波&#xff0c;记录一下以便后续的学习&#xff08;主要是怕忘了&#xff09; 教程网址cut&tag教程 背景知识&#xff1a;靶标下裂解与标记&#xff08;Cleavage Under Targets & Tagmentation&#xf…

90后医生下班摆摊就能赚1500?看内行人是如何分析的?2024普通人逆袭的机会,2024普通人想翻身的风口行业

“在自己空余的时间&#xff0c;做点自己喜欢的事情”这就是浙江义乌的王医生&#xff0c;摆摊被采访时的回答。王大夫说&#xff0c;自己兼职已经有半年多了&#xff0c;每天的营业额能达到1500元。同时王医生表示&#xff0c;自己的目标是开一间自己的小店。 看到这里&#x…

新版Idea配置仓库教程

这里模拟的是自己搭建的本地仓库环境&#xff0c;基于虚拟机搭建利用gogs创建的仓库 1、Git环境 你需要准备好git和仓库可以使用github 、gitee等 1.1 拉取代码 本项目使用 Git 进行版本控制&#xff0c;在 gogs 上创建一个个人使用的 git 仓库&#xff1a; http://192.168.…

1.5.2 基于XML配置方式使用Spring MVC

用户登录演示效果 实战概述&#xff0c;可以帮助你更好地理解整个流程。 项目创建 创建了一个名为 SpringMvcDemo01 的 Jakarta EE 项目。通过 Maven 添加了项目所需的依赖&#xff0c;包括 Spring MVC、JSTL 等。 视图层页面 创建了登录页面&#xff08;login.jsp&#xff0…

计算机毕业设计springboot体育馆场地预约管理系统【附源码】

计算机毕业设计springboot体育馆场地预约管理系统[附源码] &#x1f345; 作者主页 网顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制…

良心实用的电脑桌面便利贴,好用的便利贴便签小工具

在日常办公中&#xff0c;上班族经常需要记录临时任务、重要提醒或者突发的灵感。比如&#xff0c;在紧张的项目会议中&#xff0c;忽然想到一个改进的点子&#xff0c;或者是在处理邮件时&#xff0c;需要记下对某个客户的回复要点。在这些场景下&#xff0c;如果能直接在电脑…

基于SpringBoot+Vue的物流管理系统

运行截图 获取方式 Gitee仓库

Gitee添加仓库成员

1.进入你的项目 2.点击管理 3.左侧有个仓库管理 4.要加哪个加哪个&#xff0c;有三个方式~ 可以直接添加之前仓库合作过的开发者

In Context Learning(ICL)个人记录

In Context Learning&#xff08;ICL&#xff09;简介 In Context Learning&#xff08;ICL&#xff09;的关键思想是从类比中学习。上图给出了一个描述语言模型如何使用 ICL 进行决策的例子。首先&#xff0c;ICL 需要一些示例来形成一个演示上下文。这些示例通常是用自然语言…

react18【实战】tab切换,纯前端列表排序(含 lodash 和 classnames 的安装和使用)

技术要点 动态样式 className{tabItem ${currentType item.value && "active"}}安装 lodash npm i --save lodash使用 lodash 对对象数组排序&#xff08;不会改变源数组&#xff09; _.orderBy(dataList, "readNum", "desc")src\De…

ArcGIS10.2系列许可到期解决方案

本文手机码字&#xff0c;不排版了。 昨晚&#xff08;2021\12\17&#xff09;12点后&#xff0c;收到很多学员反馈 ArcGIS10.2系列软件突然崩溃。更有的&#xff0c;今天全单位崩溃。 ​ 提示许可15天内到期。之前大部分许可是到2021年1月1日的。 ​ 后续的版本许可都是永久的…

深度学习技术之加宽前馈全连接神经网络

深度学习技术 加宽前馈全连接神经网络1. Functional API 搭建神经网络模型1.1 利用Functional API编写宽深神经网络模型进行手写数字识别1.1.1 导入需要的库1.1.2 加载虹膜&#xff08;Iris&#xff09;数据集1.1.3 分割训练集和测试集1.1.4 定义模型输入层1.1.5 添加隐藏层1.1…

图片转表格的免费软件,这几款值得收藏!

在数字化时代&#xff0c;图片转表格的需求日益增多。无论是工作汇报、数据分析还是学术研究&#xff0c;将图片中的信息转化为表格都能极大地提高工作效率。然而&#xff0c;许多人在面对这一任务时&#xff0c;往往感到无从下手。今天&#xff0c;我将为大家推荐几款免费的图…

如何在群晖NAS中开启FTP并实现使用公网地址远程访问传输文件

文章目录 1. 群晖安装Cpolar2. 创建FTP公网地址3. 开启群晖FTP服务4. 群晖FTP远程连接5. 固定FTP公网地址6. 固定FTP地址连接 本文主要介绍如何在群晖NAS中开启FTP服务并结合cpolar内网穿透工具&#xff0c;实现使用固定公网地址远程访问群晖FTP服务实现文件上传下载。 Cpolar内…

Nginx内网环境开启https

文章目录 前言一、open-ssl1. 验证2. 安装3.生成ssl证书 一、nginx1. 验证支持模块2. 安装必要模块2.1 重新编译nginx2.2 替换原文件 3. 配置https 总结 前言 nginx开启https前提&#xff1a; 服务器支持open-sslnginx 包含--with-http_ssl_module --with-stream --with-stre…

[笔试强训day08]

文章目录 HJ108 求最小公倍数NC95 数组中的最长连续子序列DP39 字母收集 HJ108 求最小公倍数 HJ108 求最小公倍数 #include<iostream>using namespace std;int a,b;int gcd(int a,int b) {if(b0) return a;return gcd(b,a%b); } int main() {cin>>a>>b;int …

嵌入式和单片机的区别在哪?

嵌入式和单片机是两个不同的概念&#xff0c;它们在很多方面都存在着差异。嵌入式系统是一种专用的计算机系统&#xff0c;通常用于控制和监测其他设备。它通常由微处理器、存储器、输入/输出接口和其他外围设备组成。嵌入式系统可以运行各种操作系统&#xff0c;如 Linux、Win…

el-dialog设置el-head固定

0 效果 1 代码 ::v-deep .adTextDetailDialogClass .el-dialog__body{max-height: calc(100vh - 150px);overflow: auto;border-top:1px solid #dfdfdf;border-bottom:1px solid #dfdfdf; } ::v-deep .adTextDetailDialogClass .el-dialog{position: fixed;height:fit-content;…

瑞芯微 rk3588 Linux系统备份还原 StepbyStep

1.系统备份 1.1 将瑞芯微平台嵌入式系统的root ssh 权限开通 step1:sudo vi /etc/ssh/sshd_config step2: 找到PermitRootLogin,把开关打开&#xff1a; PermitRootLogin yes step3:重启ssh服务 sudo systemctl restart sshd 1.2.使用瑞芯微的打包脚本把嵌入式系统系统打包 这…