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

news2024/11/27 16:32:33

请添加图片描述

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

🍋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.coloradmin.cn/o/1671042.html

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

相关文章

Python 求高斯误差函数 erf 和 erfc

文章目录 Part.I IntroductionPart.II 概念定义Chap.I 误差函数 erfChap.II 误差函数补 erfc Part.II 求值与绘图Chap.I 求取方式Chap.II 绘图 Reference Part.I Introduction 本文将对误差函数&#xff08;ERror Function, ERF&#xff09;进行简单的介绍&#xff0c;并探索如…

R语言:GSEA分析

#安装软件包 > if (!requireNamespace("BiocManager", quietly TRUE)) install.packages("BiocManager") > BiocManager::install("limma") > BiocManager::install("org.Hs.eg.db") > BiocManager::install("…

吴恩达机器学习笔记:第 10 周-17大规模机器学习(Large Scale Machine Learning)17.1-17.2

目录 第 10 周 17、 大规模机器学习(Large Scale Machine Learning)17.1 大型数据集的学习17.2 随机梯度下降法 第 10 周 17、 大规模机器学习(Large Scale Machine Learning) 17.1 大型数据集的学习 如果我们有一个低方差的模型&#xff0c;增加数据集的规模可以帮助你获得更…

8种常见的CMD命令

1.怎么打开CMD窗口 步骤1&#xff1a;winr 步骤2&#xff1a;在弹出的窗口输入cmd&#xff0c;然后点击确认&#xff0c;就会出现一个cmd的窗口 2.CMD的8种常见命令 2.1盘符名称冒号 说明&#xff1a;切换盘的路径 打开CMD窗口这里默认的是C盘的Users的27823路径底下&#xf…

生产制造行业推拉式生产的复合应用

一、案例分析&#xff08;汽配行业&#xff09; 重点&#xff1a; 1. MTO/MTS 与 PUSH/PULL 有关系但是不是充分关系 2. MTO/MTS 是公司经营策略&#xff0c;更多是对市场需求的经营策略&#xff0c;体现在生产时机上的不同&#xff0c;一个是等客户需求&#xff0c;一个是填…

程序员健康指南:运动,让代码更流畅

程序员健康指南&#xff1a;运动&#xff0c;让代码更流畅 程序员&#xff0c;一个与电脑相伴的群体&#xff0c;长时间的久坐和高强度的脑力劳动是他们的日常。然而&#xff0c;久坐不仅影响体态&#xff0c;更对心脏健康构成威胁。根据《欧洲心脏杂志》的研究&#xff0c;中…

Caddy2使用阿里云DNS申请https证书,利用阿里云DNS境内外不同解析给Gone文档做一个同域名的国内镜像站点

我从头到尾实现了一个Golang的依赖注入框架&#xff0c;并且集成了gin、xorm、redis、cron、消息中间件等功能&#xff1b;自己觉得还挺好用的&#xff0c;并且打算长期维护&#xff01; github地址&#xff1a;https://github.com/gone-io/gone 文档原地址&#xff1a;https:/…

MTATLAB--一元线性回归分析

一文让你彻底搞懂最小二乘法&#xff08;超详细推导&#xff09; 在进行一元线性回归分析时&#xff0c;使用最小二乘法进行解题&#xff0c;关于最小二乘法具体看上述文章。 数据文件在文章顶部可见&#xff0c;将第一列数据作为自变量x&#xff0c;第二列数据作为应变量y。建…

使用java远程提交flink任务到yarn集群

使用java远程提交flink任务到yarn集群 背景 由于业务需要&#xff0c;使用命令行的方式提交flink任务比较麻烦&#xff0c;要么将后端任务部署到大数据集群&#xff0c;要么弄一个提交机&#xff0c;感觉都不是很离线。经过一些调研&#xff0c;发现可以实现远程的任务发布。…

【RabbitMQ】消息队列 - RabbitMQ的使用记录

目录 一、什么是消息队列 二、什么是RabbitMQ 三、安装RabbitMQ 3.1 安装Erlang环境 3.2 安装RabbitMQ 3.3 打开服务管理界面 3.4 常用命令 四、Python示例代码 4.1 发送数据 4.2 接收数据 一、什么是消息队列 消息队列(Message Queue)是一种用于在应用程序之间传递消…

vue:网页icon无法显示

logo文件放在public文件夹下&#xff0c;在html里设置icon。 本地源码运行后发现网页icon无法显示我们设置的logo&#xff0c;而是显示了浏览器默认icon。 这个问题不需要解决&#xff0c;部署后网页icon显示就正常了。

绝地求生PUBG新老艾伦格有什么差别 老艾伦格什么时候回归

复古风格的艾伦格原始地图携带着那些标志性的记忆符号华丽回归&#xff0c;邀请您沉浸于往昔的每一处细节探索中。我们不仅还原了游戏诞生的起点&#xff0c;还在其中巧妙融入现代游戏元素&#xff0c;构筑一座连接昔日与今朝的桥梁&#xff0c;完美融合了经典与创新的游戏体验…

【动态规划四】子序列问题

目录 leetcode题目 一、最长递增子序列 二、摆动序列 三、最长递增子序列的个数 四、最长数对链 五、最长定差子序列 六、最长的斐波那契子序列的长度 七、最长等差数列 八、等差数列划分 II leetcode题目 一、最长递增子序列 300. 最长递增子序列 - 力扣&#xff0…

关于SQL

数据库简介&#xff1a; 数据库分类 关系型数据库模型&#xff1a; 优点&#xff1a;易于维护&#xff0c;可以实现复杂的查询 缺点&#xff1a;海量数据 读取写入性能差&#xff0c;高并发下数据库的io是瓶颈 是把复杂的数据结构归结为简单的二元关系&#xff08;即二维表…

Linux字符设备驱动设计

Linux字符设备驱动设计 概述 驱动的定义与功能 计算机系统中存在着大量的设备&#xff0c; 操作系统要求能够控制和管理这些硬件&#xff0c; 而驱动就是帮助操作系统完成这个任务。 驱动相当于硬件的接口&#xff0c; 它直接操作、 控制着我们的硬件&#xff0c; 操作系统通…

工作太闲,平常有没有用手机能赚钱的,这里我推荐了4种可月入2000的副业

确实有许多通过手机赚钱的方式&#xff0c;以下是一些常见的方法 1.拍照卖图 如果你有一台相机或智能手机&#xff0c;喜欢拍照&#xff0c;那么可以将自己拍摄的图片上传到网站上&#xff0c;赚取稿费。 2. 做问卷调查 许多公司需要了解消费者的意见&#xff0c;所以会通过…

【XSRP软件无线电】基于软件无线电平台的QPSK频带通信系统设计

目录&#xff1a; 目录&#xff1a; 一、绪论 1.1 设计背景 1.2 设计目的 二、系统总体方案 2.1 专题调研题目 2.2 调研背景 2.3 设计任务解读 2.4 设计原理 2.4.1 原理框图 2.4.2 功能验证 三、软件设计 3.1 程序解读 3.2 程序设计 3.3 仿真结果&#xff1a; 四、程序代码分析…

爬取深圳2024年链家二手房数据,共3000条数据(其他城市也可)

文章目录 专栏导读1.目标2.导入相关库3.获取每个二手房的链接4.获取每个链接中的相关数据5.保存数据6.数据展示 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫…

二叉树OJ刷题

制作不易&#xff0c;三连支持一下吧&#xff01;&#xff01;&#xff01; 文章目录 前言一、相同的树二、单值二叉树三.对称二叉树四.二叉树的前序遍历五.另一棵树的子树六.二叉树遍历总结 前言 前三篇博客我们详细介绍了树形结构&#xff0c;及两种特殊的树&#xff1a;堆和…

【Ubuntu永久授权串口设备读取权限“/dev/ttyUSB0”】

Ubuntu永久授权串口设备读取权限 1 问题描述2 解决方案2.1 查看ttyUSB0权限&#xff0c;拥有者是root&#xff0c;所属用户组为dialout2.2 查看dialout用户组成员&#xff0c;如图所示&#xff0c;普通用户y不在dialout组中2.3 将普通用户y加入dialout组中2.4 再次查看dialout用…