STL模拟实现——string

news2024/12/25 13:48:59

前言

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。

STL有六大组件:算法,容器,迭代器,仿函数,空间配置器和配接器。

今天我们从string的入手,模拟实现string。

模拟实现string

我这里就不详细讲解string的具体功能是什么了,想要学习的可以通过以下网站自行学习:

https://legacy.cplusplus.com/reference/string/string/?kw=string

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;


namespace szg
{
	class string
	{
		//流输出
		friend ostream& operator<<(ostream& out, const szg::string& s);
		//流输入
		friend istream& operator>>(istream& in, szg::string& s);
	public:
		//迭代器
		typedef  char* iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

	public:
		//构造函数
		string(const char* s = "")
		{
			_size = strlen(s);
			_capcity = _size;
			_str = new char[_capcity + 1];
			strcpy(_str, s);
		}

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

		//析构函数
		~string()
		{
			delete[] _str;
		}

		//赋值重载
		string& operator=(const string& s)
		{
			string temp(s);
			swap(temp);
			return *this;
		}

		//容量扩展
		void reserve(int n)
		{
			char* temp = new char[n + 1];
			strcpy(temp, _str);
			delete[] _str;
			_str = temp;
		}

		//尺寸扩展
		void resize(size_t n, char c = '\0')
		{
			if (n > _capcity)
			{
				reserve(n);
				_capcity = n;
			}
			for (size_t i = _size + 1; i <= n; ++i)
			{
				_str[i] = c;
			}
		}
		 
		size_t size()const
		{
			return _size;
		}

		const char* c_str()const
		{
			return _str;
		}
		char& operator[](size_t n)
		{
			assert(n >= _size);
			return _str[n];
		}

		const char& operator[](size_t n)const
		{
			assert(n >= _size);
			return _str[n];
		}
		//修改内容
		void push_back(const char c)
		{
			if (_size == _capcity)
			{
				_capcity = 2 * _capcity + 1;
				reserve(_capcity);
			}
			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
		}

		void append(const char* s)
		{
			size_t len = strlen(s);
			if (_capcity < len + _size)
			{
				_capcity = len + _size;
				reserve(_capcity);
			}
			strcat(_str, s);
			_size += len;
		}

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


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

		void swap(string& s)
		{
			size_t temp = s._size;
			s._size = _size;
			_size = temp;
			temp = s._capcity;
			s._capcity = _capcity;
			_capcity = temp;
			char* t = s._str;
			s._str = _str;
			_str = t;
		}

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

		bool empty()const
		{
			if (_size == 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}


		bool operator==(const string& s)const
		{
			if (strcmp(_str, s._str) == 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		bool operator>(const string& s)const
		{
			if (strcmp(_str, s._str) > 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		bool operator<(const string& s)const
		{
			if (strcmp(_str, s._str) < 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		bool operator>=(const string& s)const
		{
			if (strcmp(_str, s._str) >= 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		bool operator<=(const string& s)const
		{
			if (strcmp(_str, s._str) <= 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		size_t find(char c, size_t pos = 0)const
		{
			int cur = pos;
			while (cur < _size)
			{
				if (_str[cur] == c)
				{
					return cur;
				}
				cur++;
			}
			return -1;
		}

		size_t find(const char* s, size_t pos = 0)const
		{
			char* t = strstr(_str + pos, s);
			if (t)
			{
				return t - _str;
			}
			return -1;
		}

		string& insert(size_t pos, char c)
		{
			assert(pos > _size);
			if (_size == _capcity)
			{
				_capcity = _capcity == 0 ? 4 : _capcity * 2;
				reserve(_capcity);
			}
			int cur = _size + 1;
			while (cur > pos)
			{
				_str[cur] = _str[cur - 1];
				--cur;
			}
			_str[pos] = c;
			_size++;
			return *this;
		}

		string& insert(size_t pos, char* s)
		{
			assert(pos > _size);
			size_t len = strlen(s);
			if (_size + len < _capcity)
			{
				_capcity = _size + len;
				reserve(_capcity);
			}
			int cur = _size + len + 1;
			while (cur > pos + len)
			{
				_str[cur - 1] = _str[cur - len - 1];
				cur--;
			}

			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}
		string& erase(size_t pos, size_t len)
		{
			if (pos + len >= _size)
			{
				_size = pos;
				_str[pos] = '\0';
			}
			else
			{
				for (int i = pos + len; i < _size; ++i)
				{
					_str[i - len] = _str[i];
				}
				_size -= len;
				_str[_size] = '\0';
			}
		}

	private:
		char* _str;
		size_t _size = 0;
		size_t _capcity = 0;
        const static size_t npos = -1;

	};

	//IO流输入输出
	ostream& operator<<(ostream& out, const szg::string& s)
	{
		out << s._str;
		return out;
	}
	istream& operator>>(istream& in, szg::string& s)
	{
		s.clear();
		char c;
		in.get(c);
		while (c != ' ' && c != '\n')
		{
			s.push_back(c);
			in.get(c);
		}
		return in;
	}
}

其中大部分都是数据结构的内容,但是有些知识还是需要单独讲解。

迭代器

迭代器的出现是为了不同的数据结构可以采用统一的方法去遍历数据,降低了使用成本。

string的迭代器是原生指针,但并不是所有STL结构都采用的是原生指针,还有许多使用的是类来封装迭代器。

让我们先来看看官方的迭代器是怎么使用的。

void test1()
{
	string s1;

	s1.push_back('a');
	s1.append("98176428233");
	s1 += "ddddddd";
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;
	for (auto e : s1)
	{
		cout << e;
	}
	cout << endl;
}

 我们可以看出两点:

第一点是迭代器的用法,使用*获取数据,使用++遍历迭代器,并且这个过程可以读取也可以修改数据。

第二点:是范围for使用的结果和使用迭代器遍历的结果完全一致,范围for的本质与迭代器完全一样,只是编译器将范围for先转换为上面的样子。

void test2()
{
	string s1;

	s1.push_back('a');
	s1.append("98176428233");
	s1 += "ddddddd";

	cout << s1 << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		(*it)++;
		++it;
	}
	cout << s1 << endl;
	for (auto e : s1)
	{
		--e;
	}
	cout << s1 << endl;
}

 第二个测试有有新的问题了,为什么明明范围for中的数据被改变了,但是打印出来的结果却没有改变?这是由于这个e并不是数据的引用,而是拷贝,e的改变并不会导致s1的改变,如果需要改变内容需要下列的代码:

for (auto& e : s1)
{
	--e;
}

string的迭代器非常简单,使用原生指针重命名就可以了。

typedef  char* iterator;
void test3()
{
	szg::string s1;

	s1.push_back('a');
	s1.append("98176428233");
	s1 += "ddddddd";

	cout << s1 << endl;
	szg::string::iterator it = s1.begin();
	while (it != s1.end())
	{
		(*it)++;
		++it;
	}
	cout << s1 << endl;
	for (auto& e : s1)
	{
		--e;
	}
	cout << s1 << endl;
}

上面我们自己模拟实现的string与官方提供的,最后运行结果完全一样。

insert

官方插入函数:

 其中npos一般来说是-1,但也不一定。

const static size_t npos = -1;

在模拟实现的string中npos是静态成员变量,而且只有整型变量可以在类中定义,其他静态变量只能在全局域定义。

        string& insert(size_t pos, char c)
		{
			assert(pos > _size);
			if (_size == _capcity)
			{
				_capcity = _capcity == 0 ? 4 : _capcity * 2;
				reserve(_capcity);
			}
			int cur = _size + 1;
			while (cur > pos)
			{
				_str[cur] = _str[cur - 1];
				--cur;
			}
			_str[pos] = c;
			_size++;
			return *this;
		}

		string& insert(size_t pos, char* s)
		{
			assert(pos > _size);
			size_t len = strlen(s);
			if (_size + len < _capcity)
			{
				_capcity = _size + len;
				reserve(_capcity);
			}
			int cur = _size + len + 1;
			while (cur > pos + len)
			{
				_str[cur - 1] = _str[cur - len - 1];
				cur--;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

而且两个插入函数要非常注意边界条件,特别是pos==0的情况。

有些同学一开始可能会写成下列情况:


			size_t cur = _size;
			while (cur >= pos)
			{
				_str[cur + 1] = _str[cur];
				--cur;
			}




			size_t cur = _size;
			while (cur >= pos)
			{
				_str[cur + len] = _str[cur];
				cur--;
			}

然后就死循环了。

除了本文的解决方法,还有一种方法可以解决。


			int cur = _size;
			while (cur >= (int)pos)
			{
				_str[cur + 1] = _str[cur];
				--cur;
			}




			int cur = _size;
			while (cur >= (int)pos)
			{
				_str[cur + len] = _str[cur];
				cur--;
			}

大家可以好好感悟一下。

流输入

    istream& operator>>(istream& in, szg::string& s)
	{
		s.clear();
		char c;
		in.get(c);
		while (c != ' ' && c != '\n')
		{
			s.push_back(c);
			in.get(c);
		}
		return in;
	}

流输入输出都是类的友元函数,而且输入流由于需要提取'  '和'\n',需要使用in.get(c)来提取,类似于C语言中的getchar()。

并且流输入还有改进的空间:

    istream& operator>>(istream& in, szg::string& s)
	{
		s.clear();
		char buff[128] = { '\0' };
		int k = 0;
		in.get(buff[k]);
		while (buff[k] != ' ' && buff[k] != '\n')
		{
			if (k == 127)
			{
				s += buff;
				k = -1;
			}
			in.get(buff[++k]);
		}
		buff[k + 1] = '\0';
		s += buff;
		return in;
	}

我们可以看出,改进后避免了在小容量区间频繁地插入和扩容,已空间换时间,提高了效率。

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

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

相关文章

从0搭建一个WebRTC,实现多房间多对多通话,并实现屏幕录制

这篇文章开始会实现一个一对一WebRTC和多对多的WebRTC&#xff0c;以及基于屏幕共享的录制。本篇会实现信令和前端部分&#xff0c;信令使用fastity来搭建&#xff0c;前端部分使用Vue3来实现。 为什么要使用WebRTC WebRTC全称Web Real-Time Communication&#xff0c;是一种实…

安全狗重磅发布数据安全解决方案·数垒

一、 势在必行的数据安全 近年来&#xff0c;随着《网络安全法》、《数据安全法》、“数据二十条”等多部法律、意见法规等的相继颁布&#xff0c;数据安全管理与防护在国家政策上成为势在必行的行动之一。与此同时&#xff0c;伴随着数字经济时代的到来&#xff0c;数以万计…

Java基础语法-学习笔记

目录 01Java语言的发展 02Java的三大平台 03Java的主要特性 04JRE和JDK 1. 注释 使用的技巧 注意点 2. 关键字 2.1 概念 2.2 第一个关键字class 3. 字面量 区分技巧 4. 变量 4.1 什么是变量&#xff1f; 4.2 变量的定义格式 5. 数据类型 5.1 Java语言数据类型的…

STM32——TIM输出比较

文章目录一、TIM输出比较输出比较简介PWM简介输出比较通道(高级)输出比较通道(通用)输出比较模式控制器工作原理PWM基本结构参数计算三、PWM驱动LED呼吸灯电路设计关键代码关键函数与参数引脚重映射取消默认调试功能函数极性选择决定占空比&#xff0c;周期的三个函数四、PWM驱…

Design pattern-js的设计模式(一)

前言 什么是设计模式&#xff1f;&#xff08;Design pattern&#xff09;代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间…

【youcans 的 OpenCV 例程 300篇】254.OpenCV 绘制图像标记

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】254. OpenCV 绘制标记 7.1 绘图函数基本参数 OpenCV提供了绘图功能&#xff0c;可以在图像上绘制直线、矩形、圆、椭圆等各种几何图形。 函数 cv.line()、cv.rectangle()、cv.circle()、cv.polyli…

深度学习:08 训练、测试和验证集的说明

目录 用于深度学习的数据集 训练集 验证集 测试集 总结 用于深度学习的数据集 接下来&#xff0c;我将在这篇里面讨论在训练和测试神经网络期间使用的不同数据集。 出于模型的训练和测试目的&#xff0c;我们应该将数据分解为三个不同的数据集。这些数据集将包含以下内容…

【小f的刷题笔记】(JS)阶乘 - 阶乘后的零 LeetCode172 阶乘函数后K个零 LeetCode793

【阶乘】 一、阶乘后的零&#xff1a; LeetCode172 链接&#xff1a; 172.阶乘后的零 题目&#xff1a; 思路&#xff1a; 0的产生是一定是因为2*5产生的&#xff0c;所以就是找因数 并且&#xff0c;可想而知&#xff0c;找的到因数5&#xff0c;必然找的到因数2与之搭配…

【MySQL】深入理解B+树索引

文章目录1. 前言2. 索引方案3. InnoDB的索引方案4. 索引的分类4.1 聚簇索引4.2 二级索引4.3 联合索引5. InnoDB中的B树索引的注意事项5.1 内节点中目录项记录的唯一主5.2 一个页至少容纳2条记录6. MyISAM中的索引⽅案简单介绍1. 前言 索引&#xff0c;是MySQL快速查询的秘籍。…

ARMv8/ARMv9:深入理解MPIDR_EL1寄存器中的affinity

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:【精选】ARMv8/ARMv9架构入门到精通-[目录] 👈👈👈官方文档(ARM ARM文档)的介绍如下所示 翻译一下MPIDR_EL1相关的英文,如下所示: 作用: 在多处理器系统中,为调…

Xshell 连接虚拟机(Ubuntu、CentOS)

对于一些linux的初学者来说&#xff0c;在没有自己的服务器时可以选择使用虚拟机来代替&#xff08;如ubuntu、centos等&#xff09;进行相关的学习。下面介绍下如何使用xshell来远程连接虚拟机。 注意&#xff1a;下面我以Ubuntu来举例说明。 1、创建虚拟机 虚拟机的创建网络…

1、数据库安装修改root密码管理自启服务

MySQL的下载和安装 登录MySQL官网下载MySQL.zip包 MySQL :: Download MySQL Community Server 下载完毕可自行选择存储位置&#xff0c;进行解压 解压后配置环境变量 完成配置后在MySQL目录下新建一个my.ini配置文件 文件写入以下内容 [client] # 设置mysql客户端默认字符集…

面向对象2(static修饰变量和方法、Javabean类、测试类和工具类、对main方法的理解、继承、子类继承父类构造方法变量和方法)

1、static修饰变量和方法 JDK8以前&#xff0c;静态区在方法区里面&#xff0c;JDK8开始&#xff0c;静态区挪到了堆内存当中 理解&#xff1a; 因为静态方法没有this&#xff0c;而非静态方法是有一个隐含的参数this的&#xff0c;所以想在静态方法里面调用非静态变量或方法就…

电子招标采购系统源码之传统采购模式面临的挑战

采购类型多 采购制度&#xff1a;采购金额、部门、品类的差异导致管理标准不同。 采购流程&#xff1a;从供应商管理、寻源操作到合同签订、订单执行&#xff0c;业务流程长&#xff0c;审批节点多&#xff0c;传统管理透明度低&#xff0c;联动性差。 供应商管理难 寻源&#…

亚马逊云科技 Build On - Serverless助力企业降本增效

亚马逊云科技 Build On - Serverless开启零售新篇章梅开三度活动体验实验问题总结一点建议咖啡案例实验Serverless学习总结梅开三度 Hi,作为一名Builder&#xff0c;这也是第三次参加由AWS&CSDN共同举办的Build On活动&#xff0c;跟前几期一样&#xff0c;活动举办方也是…

Java集合ArrayList-学习笔记

目录 ArrayList 集合和数组的优势对比&#xff1a; 1.1 ArrayList类概述 1.2 ArrayList类常用方法 1.3 ArrayList存储字符串并遍历 1.4 ArrayList存储学生对象并遍历 1.5 查找用户的索引 1.6 判断用户的是否存在 ArrayList 集合和数组的优势对比&#xff1a; 集合长度可…

Ardupilot EKF3核心算法《状态量速度与位置预测方程》

目录 文章目录 目录摘要1. Ardupilot EKF3核心算法《状态量速度预测方程》2. Ardupilot EKF3核心算法《状态量位置预测方程》3. Ardupilot EKF3核心算法《状态量速度与位置预测方程》摘要 本节主要记录Ardupilot EKF3核心算法《状态量速度与位置预测方程》的过程,欢迎批评指正…

mysql生产数据库被误删

23年的头一天上班安装数据库&#xff0c;因为ssh工具来回切换失误&#xff0c;犯下不可饶恕的错误&#xff0c;居然将生产数据库全部删除&#xff0c;工作十几年头一次干这种蠢事&#xff0c;第一时间反应是一世英名毁于一旦&#xff0c;赶紧跑路。第二反应还是想办法看能否挽回…

aws eks 使用 cloudformation 创建并更新自管节点组

参考资料 更新现有自行管理的节点组 使用eks自管节点组能够最大程度控制节点的各项配置和参数&#xff0c;包括并不限于ami&#xff0c;节点类型等 但是使用自管节点也给用户带来了较大的维护和更新成本。对于节点组的更新操作&#xff0c;我们可以使用cloudformation的方式…

速览Visual Studio 2022 中的新增功能

目录 性能改进 Visual Studio 2022 为 64 位 在文件中更快地查找 Git 工具速度更快 生成新式应用 适用于 C、.NET 和热重载的更佳开发工具 Blazor & 的汇报ASP.NET 的 Razor 编辑器 热重载 创新触手可及 Git 多存储库支持和行暂存支持 IntelliCode 改进 为每个…