C++ 之 string类的模拟实现

news2024/11/24 20:11:53

这学习我有三不学

昨天不学,因为昨天是个过去

明天不学,因为明天还是个未知数

今天不学,因为我们要活在当下,我就是玩嘿嘿~

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

目录

一、string类的模拟实现

1.成员函数(Member functions)

1.1 构造函数(constructor)

1.2 析构函数(destructor)

1.3 赋值拷贝函数(operator=)

2.迭代器(iterators)

3.容量(Capacity)

4.元素访问(Element access)

5.调节器(Modifiers)

6.字符操作(String operation)

7.成员常量(Member constant)npos实现

8.非成员函数重载(Non-member function overload)

二、完结撒❀


前言:

模拟string类的实现对于我们学习认识string类会有更加深刻的理解,还没学过string类的老铁建议可以先看学习一下我的上一篇博客讲解:C++ 之 string类 详细讲解,再来进行模拟实现。

一、string类的模拟实现

在上篇博客中讲解了string类的常用接口,这篇博客带大家模拟实现一下string类的一些常用接口。

string类查阅文档

我们根据上面文档所规划的接口分类为大家进行部分模拟实现,大家可以先简单看一下上面文档。

1.成员函数(Member functions)

1.1 构造函数(constructor)

● 无参构造函数 string() 实现:

string()
	//:_str(nullptr)
	:_str(new char[1])//不能赋空指针,因为直接c_str会出错
	, _size(0)
	, _capacity(0)
{
	_str[0] = '\0';
}

有参(字符串)构造函数 string(const char* str = "") 实现:

//string(const char* str = nullptr)错
//string(const char* str = '\0')错
string(const char* str = "")//常量字符串默认结尾含有\0
//该构造函数可以替代上面无参构造函数
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

● 拷贝构造函数 string (const string& s) 实现:
 

//s2(s1)
string (const string& s)
{
	_str = new char[s._capacity+1];//完成深拷贝,+1 存放\0使用
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

1.2 析构函数(destructor)

● ~string()实现:

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

1.3 赋值拷贝函数(operator=)

● operator=实现:

	//s2 = s1
	string& operator=(const string& s)
	{
		char* tmp = new char[s._capacity + 1];//多开辟的1个空间用来存储\0
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;

		return *this;//支持连续赋值
	}

2.迭代器(iterators)

● begin实现:

在string类中迭代器中begin表示的就是字符串的首地址的指针,但并不是所有迭代器都是由指针来实现的。

这里使用迭代器的对象有两种,一种是const修饰的,一种是const没有修饰的,所以begin实现应有const修饰,和const不修饰两种:

typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
	return _str;
}

const_iterator begin() const
{
	return _str;
}

● end实现:

同理,end实现也一样:

typedef char* iterator;
typedef const char* const_iterator;

iterator end()
{
	return _str + _size;
}

const_iterator end() const
{
	return _str + _size;
}

这里可以再说一下,范围for的实现也是基于迭代器begin和end所实现的,大家可以在汇编代码中就可以看到。

而对于const修饰的对象和const没有修饰的对象分别对应使用的范围for其内部实现也是const修饰的begin,end和const没有修饰的begin,end两种范围for。

3.容量(Capacity)

● size实现:

size_t size() const
{
	return _size;
}

● capacity实现:

size_t capacity() const
{
	return _capacity;
}

● resize实现:
由于resize函数的实现可能需要对数组进行扩容,所以我们先实现一下reserve函数:

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n+1];//多开1个位置给\0
		strcpy(tmp, _str);
		delete[] _str;//!!!
		_str = tmp;

		_capacity = n;
	}
}

注意:reserve只有在n大于当前有效空间是才会进行开辟,当n小于当前有效空间不会进行任何操作。

resize实现:

void resize(size n,const char* c)
{
    if(n>_size)
    {
        reserve(n);
        for(size i=size; i<n;i++)
        {
            _str[i] = c;
        }
        _str[n] = '\0';
        _size = n;
    }
    else
    {
        _str[n] = '\0'; 
        _size = n;
    }
}

● clear实现:

注意:clear只是清楚当前所存数据,而不是销毁空间

void clear()
{
	_size = 0;
	_str[0] = '\0';
}

4.元素访问(Element access)

● operator[]实现:

operator[]的实现功能就是访问string类里面字符串的字符所以实现并不复杂:
 

char& operator[](size_t pos)
{
	assert(pos < _size);

	return _str[pos];
}

const char& operator[](size_t pos) const
{
	assert(pos < _size);

	return _str[pos];
}

5.调节器(Modifiers)

● insert实现:

插入字符或字符串那么肯定会涉及到空间不够是否需要扩容的问题,解决了之后剩下的就是将插入位置之后的字符串向后移动插入字符或字符串大小的位置,为要插入的字符或字符串流出插入空间,之后再将字符或字符串插入即可:

void insert(size_t pos, char ch)
{
	assert(pos <= _size);

	if (_size == _capacity)
	{
		//扩容。。。reserve
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	//后移字符串方案1
	//int end = _size;
	//while (end >= (int)pos)//!!!一个运算符两边操作数类型不同的时候发生类型提升(范围小的像范围大的提升 这里有符号向无符号提升)
	//{
	//	_str[end + 1] = _str[end];
	//	--end;
	//}

	//后移字符串方案2
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	_size++;
}

void insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	size_t end = _size + len;
	while (pos < end - len + 1)
	{
		_str[end] = _str[end - len];
		--end;
	}

	strncpy(_str + pos, str, len);
	_size += len;
}

● append实现:
既然已经实现了insert,那么我们直接赋用insert实现append,push_back,operator+=即可:

	void append(const char* str)
	{
		//size_t len = strlen(str);
		//if (len + _size > _capacity)
		//{
		//	//扩容。。。
		//	reserve(_size + len);
		//}

		//strcpy(_str + _size, str);
		//_size += len;

		insert(_size, str);
	}

● push_back实现:

void push_back(const char ch)
{
	//if (_size == _capacity)
	//{
	//	//扩容。。。reserve
	//	reserve(_capacity == 0 ? 4 : _capacity * 2);
	//}

	//_str[_size] = ch;
	//_str[_size + 1] = '\0';
	//++_size;

	insert(_size, ch);
}

● operator+=实现:
 

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

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

● erase实现:

当len要清除的字符长度大于等于总字符长度减去pos起始位置时就要将pos起始位置后面的字符全都清除,即_str[pos] = '\0'即可。

	void erase(size_t pos = 0, size_t len = npos)
	{
		assert(pos < _size);
		
			if (pos == npos || pos >= _size - len)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
	}

6.字符操作(String operation)

● c_str实现:

const char* c_str() const
{
	return _str;
}

● find实现:

size_t find(const char c, size_t pos = 0) const
{
	assert(pos < _size);

	//从pos位置开始遍历字符串
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == c)
		{
			return i;
		}
	}
	return npos;
}

size_t find(const char* sub, size_t pos = 0) const
{
	assert(pos < _size);

	const char* p = strstr(_str+pos, sub);
	if (p)
	{
		return p - _str;//指针-指针为两指针之间的距离
	}
	return npos;
}

● substr实现:


与erase分析相似,当要截取的len长度的字符大于等于总字符长度_size减起始位置pos时,就要将pos后面的字符全部截取进行返回:

	string substr(size_t pos = 0, size_t len = npos) const
	{
		string sub;
		if (len >= _size - pos)
		{
			for (size_t i = pos; i < _size; i++)
			{
				sub += _str[i];
			}
		}
		else
		{
			for (size_t i = pos; i < pos + len; i++)
			{
				sub += _str[i];
			}
		}
		return sub;
	}

7.成员常量(Member constant)npos实现:

在标准库中,npos为静态全局变量,在npos文档中就有说明,其值应为size_t类型的-1。

private:
	char* _str = nullptr;
	size_t _size = 0;
	size_t _capacity = 0;

	public:
		static size_t npos;//类内声明
};

size_t string::npos = -1;//类外定义

8.非成员函数重载(Non-member function overload)

● swap实现:


在C++库中是有swap函数的,我们可以直接调用使用,但是对于自定义类型来说就比如string类,其直接调用库中的swap函数进行交换的话会调用3次拷贝构造+1次析构(库中的swap实现方式是创建一个新的临时变量tmp进行两个值的交换),这样消耗会很大,所以对于自定义类型我们不推荐直接调用库里面的swap函数。

那么我们该如何实现swap函数呢?我们可以直接对自定义类型里面的内置成员变量进行交换即可满足两者自定义类型的交换,将swap函数定义为非成员函数是因为要满足swap(s1,s2);交换的格式。

内部成员函数:

	void swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

非成员函数(全局函数):

void swap(string& s1, string& s2)
{
	s1.swap(s2);//直接调用成员函数即可
}

● operator<<实现:

ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;//实现连续打印
}

● operator>>实现:

istream& operator>>(istream& in, string& s)
{
	s.clear();//输入变量值之前需要将变量原先所存的值给清除
	char ch;
	ch = in.get();//可以获取空格' ',cin不支持获取空格

	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		ch = in.get();
	}

	return in;//实现连续赋值
}

● getline实现:

istream& getline(istream& in, string& s)
{
	s.clear();

	char c;
	c = in.get();
	//栈区开辟空间,栈区开辟空间比堆区开辟空间高
	char ch[128];//提高效率
	size_t i = 0;

	while (c != '\n')
	{
		ch[i++] = c;

		if (i == 127)
		{
			ch[127] = '\0';
			s += ch;
			i = 0;
		}
        c = in.get();
	}

	if (i > 0)
	{
		ch[i] = '\0';
		s += ch;
	}
	return in;
}

上面所实现的都是string类的常用的一些接口,其他一些没有实现的大家感兴趣的话可以查阅其他资料自行模拟实现一下

二、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤

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

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

相关文章

springboot笔记一:idea社区版本创建springboot项目的方式

社区idea 手动maven 创建springboot项目 创建之后修改pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…

Microsoft Edge浏览器:高效、简洁、个性化的网页浏览体验

Microsoft Edge是微软公司推出的一款网络浏览器&#xff0c;它是基于Chromium开源项目开发的&#xff0c;因此与Google Chrome有很多相似之处。以下是一些使用Microsoft Edge的心得体会&#xff1a; 1. 界面简洁&#xff1a;Microsoft Edge的界面设计非常简洁&#xff0c;用户…

R语言--图形绘制

一&#xff0c;绘制简单图形 c1<- c(10,20,30,40,50) c2<-c(2,7,15,40,50) plot(c1,c2,typeb) 具体参数请参考R语言中的绘图技巧1&#xff1a;plot()函数参数汇总_r语言plot参数设置-CSDN博客 c1<- c(10,20,30,40,50) c2<-c(2,7,15,40,50) plot(c1,c2,typeb,col#…

【北京迅为】《iTOP龙芯2K1000开发指南》-第四部分 ubuntu开发环境搭建

龙芯2K1000处理器集成2个64位GS264处理器核&#xff0c;主频1GHz&#xff0c;以及各种系统IO接口&#xff0c;集高性能与高配置于一身。支持4G模块、GPS模块、千兆以太网、16GB固态硬盘、双路UART、四路USB、WIFI蓝牙二合一模块、MiniPCIE等接口、双路CAN总线、RS485总线&#…

信息化还是数字化?

从 PC 互联网到移动互联网&#xff0c;再到物联网和最近流行的人工智能&#xff0c;科技的进步正在不断地改变着我们的生活和工作方式。这个过程实际上也是信息化和数字化的演进过程&#xff0c;许多人会问那信息化和数字化有啥区别&#xff1f;作为企业&#xff0c;如何在浪潮…

机器人操作系统ROS2学习—编译工作空间colcon build报错问题

在ROS2中&#xff0c;工作空间创建完成后&#xff0c;会经常需要编译工作空间。在工作空间dev_ws 下打开一个终端&#xff0c;通过指令Colcon build来编译工作空间。 1、这个过程有可能会出现如下错误: "colconbuild:Duplicate package names not supported" 根据…

openstack-自动化部署 9

所使用的kolla-ansible版本文档&#xff1a; Quick Start — kolla-ansible 14.10.1.dev21 documentation (openstack.org) 创建一个新的虚拟机 配置主机 安装docker 拉取openstack所需的组件镜像 安装ansible 配置ansible 启用lvm需要将另一个磁盘打成pv&#xff0c;以创建成…

如何与卫星影像叠加导出?

让每个人都有自己的地图&#xff01; 水经微图&#xff08;以下简称“微图”&#xff09;网页版&#xff0c;无需安装就可以绘制地图&#xff0c;得到了越来越多用户的亲睐。 但是&#xff0c;当我们千辛万苦绘制出来的地图&#xff0c;该如何与卫星影像叠加导出呢&#xff1…

【redis】Redis数据类型(一)——String类型(包含redis通用命令)

目录 Redis通用命令String类型常用的操作命令一些特殊命令详解setnx示例使用 setrange示例 mset示例 msetnx示例 append示例 getset示例 incr示例使用1.计数器2.限速器 bitcount示例使用&#xff1a;使用 bitmap 实现用户上线次数统计性能 String类型String类型简介String类型的…

Akamai 分布式“云+边缘”,打造下一代数字化基座

当下&#xff0c;数字化基础设施正逐步向分布式部署演化&#xff0c;云计算与边缘计算正在成为两大技术支柱。Gartner 数据显示&#xff0c;云服务占 IT 整体支出比例连年上涨&#xff0c;在过去一年已增长至12.1%&#xff1b;IDC 报告显示&#xff0c;截至2021年已有超过500亿…

【重磅开源】MapleBoot项目启动部署

基于SpringBootVue3开发的轻量级快速开发脚手架 &#x1f341;项目简介 一个通用的前、后端项目模板 一个快速开发管理系统的项目 一个可以生成SpringBootVue代码的项目 一个持续迭代的开源项目 一个程序员的心血合集 度过严寒&#xff0c;终有春日&#xff…

算法学习002-填数游戏 中小学算法思维学习 信奥算法解析 c++实现

目录 C填数游戏 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C填数游戏 一、题目要求 1、编程实现 在小学奥数中经常会看到一些填数字的游戏&#xff0c;如下图所示&#xff0c;其中每个…

windows下pysqlite3安装

pysqlite3 下载地址&#xff1a;SQLite Download Page windows下安装 首先在官网中下载以下文件 sqlite-amalgamation-3450300.zip #源码文件 sqlite-dll-win-x64-3450300.zip # 根据系统选择32或者64&#xff0c;可通过查看我的电脑属性中查看 sqlite-tools-win-x64-345…

VSCODE通过SFTP链接VM进行开发

在vscode插件里面搜索sftp&#xff0c;安装。 安装之后&#xff0c;按ctrlshiftp&#xff0c;找到sftp的config 然后填写刚刚的IP&#xff0c;然后是你的用户名密码 如果是通过密钥链接的话就是这样配置 然后切换到这个sftp的tab里面 然后在你的项目右键&#xff0c;然后选择op…

线上盲盒小程序:未来发展趋势与前景展望

随着互联网技术的飞速发展和消费者对于个性化、娱乐化消费体验的不断追求&#xff0c;线上盲盒小程序应运而生&#xff0c;并逐渐成为一种新兴的购物和娱乐方式。本文将对线上盲盒小程序的发展趋势和前景进行展望&#xff0c;以期为相关从业者提供有益的参考。 一、线上盲盒小…

最新 COCO数据集的下载、使用方法demo最新详细教程

&#x1f4f8; 最新 COCO数据集的下载、使用方法demo最新详细教程 &#x1f310; 文章目录 &#x1f4f8; 最新 COCO数据集的下载、使用方法demo最新详细教程 &#x1f310;摘要引言正文&#x1f4d8; COCO数据集概览&#x1f680; 下载和设置COCO数据集环境准备下载数据集 &am…

推荐:两个工作利器Snipaste和ScreenToGif

引言 本文为推荐文&#xff0c;个人工作中使用的两款工具&#xff0c;介绍推荐之。 Snipaste 简介 Snipaste是一款简单但功能强大的截图工具。 免费版功能特点&#xff1a; 矩形截图&#xff0c;自动检测窗口&#xff0c;无需手动选区。 添加矩形、椭圆形状&#xff0c;可设置…

QT httpServer多线程后台服务器的例子实现

1.需求 1.1 用户需要其他平台&#xff08;web端&#xff09;调用Qt平台的接口&#xff0c;获取想要的数据并实时显示在网页里&#xff0c;比如实时的温湿度&#xff0c;用户数据等 1.2 用户需要在其他平台&#xff08;web端&#xff09;调用Qt平台的接口&#xff0c;下发数据…

值得收藏!AMI BIOS蜂鸣声代码一览表,让你在排除故障时有的放矢

序言 AMI BIOS是美国Megatrends公司生产的一种BIOS。许多流行的主板制造商已经将AMI的BIOS集成到他们的系统中。 其他主板制造商已经创建了基于AMI BIOS系统的自定义BIOS软件。基于AMI BIOS的BIOS发出的蜂鸣声代码可能与下面的真实AMI BIOS蜂鸣声代码完全相同,也可能略有不同…

Eclipse内存分析器 Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用

1.visualvm实时监测 2.Memory Analyzer Tool打开 3.工具的使用可以参考 Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用 ------------------------ 1.我远程发现是其中一个客户端A请求服务器页面响应&#xff0c;一直得不到响应&#xff0c;然后客户端A一直请求&am…