string类的模拟实现

2023-10-31

namespace swx
{
	// string需要考虑完善的增删查改
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		const_iterator begin() const
		{
			return _str;
		}

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

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//默认构造函数是将_str指向一个空字符串,用全缺省的默认构造函数更灵活
		 //String(const char* str = "\0") 错误示范,这表示字符串有0,并不是空字符串
		 //String(const char* str = nullptr) 错误示范,nullptr并不表示空字符串
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//传统写法的拷贝构造和赋值运算符重载,老老实实干活,该开空间自己开空间,该拷贝数据就自己拷贝数据
		string(const string& s)
			:_size(strlen(s._str))
			, _capacity(_size)
		{
			// 拷贝构造函数,对_str的初始化一步完成不了,我们在函数体中进行初始化
			//这是深拷贝
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}


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

			return *this;
			//此处返回的是引用,返回引用的好处是:1.减少拷贝 2.可以对返回对象进行修改
		}
	
		
		//现代写法的拷贝构造和赋值运算符重载
		//一样要完成深拷贝,但是自己不想干活,安排别人干活,然后窃取劳动成果去,资本行为,剥削行为!
		//先写一个swap函数
		void swap(string& s)
		{
			std::swap(_str,s._str);
			std::swap(_size,s._size);
			std::swap(_capacity,s._capacity);
		}
		
		string(const string& s)
		//一定要记得初始化,不初始化,换了个随机值,析构的时候会报错
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);//调用默认构造函数来完成深拷贝
//这里不要用库里的swap函数,库里的swap函数是一种深拷贝,效率太低,用String类域的swap函数,直接换底层即可
			swap(tmp);
		}
		
		string& operator=(const string& s)
		{
			if(this != &s)
			{
				string tmp(s._str);
				swap(tmp);
			}
			return *this;
		}
		
		//更简洁的赋值运算符现代版写法
		string& operator=(string s)
		{
			//传参的时候就进行了拷贝构造,然后直接进行交换即可
			//在传参的时候已经完成了深拷贝了,所以这里判不判段是否是给自己赋值也无所谓
			swap(s);
			return *this;
		}

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

		const char* c_str() const
		{
			return _str;
		}
		
/*普通对象可以调用const成员函数,因为这是权限的缩小,但是const对象不能调用普通成员函数,因为这是权限的放大,对于[]运算符我们需要些const和非const版本,因为对于普通对象而言它会调用非const的运算符重载函数获取可以修改的返回值(会调用更加匹配的),对于const对象,会调用const的运算符重载函数获取不可修改的返回值(会调用更加匹配的)。*/
		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}
//一般const成员函数对应的返回值类型也是const的,错误实现导致const对象也可以被修改
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}
/*如果使用该运算符的string是const的那么就需要使用这个const版本的运算符重载。值得注意的是,原来返回引用,是因为函
数结束后,变量还存在,那么我们直接返回它本身,可以减少拷贝,这里返回引用的确也可以减少拷贝,但主要的是为了使返回的变
量能够被修改,如果返回的不是引用,那么返回的就是拷贝的临时变量,临时变量具有常性,是不可以被修改的。*/

	//这里我们还需要注意一个问题,[]与at的功能是一样的。

	//s[3]与s.at(3)的功能都是一样的
	
 	//但是它们处理错误的方式不同。

	//s[100]越界,那么[]会直接报错
	//s.at(100)越界,at函数会抛出一个异常


		/*对于size函数,我们只需要写一个const函数即可,因为const对象和非const对象都可以调用该函数。*/
		
		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}

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

				_capacity = n;
			}
		}
		
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				if (n > _capacity)
				{
					reserve(n);
				}

				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}	
		}
		
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

		/*size_t end = _size;
		while(end >= pos)
		{
			_sttr[end+1] = _str[end];
			--end;
			这段代码是有问题的,因为end是一个无符号数,如果pos为0,当它减为-1时相当于达到了整形的最大值,此时访问就越界了*/
			
		/*	int end = _size;
			while (end >= pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}
			这样做也是不行的,因为end是int,pos是unsigned int,二者发生关系运算的时候会发生整形提升,那么int类型的end又提升为了 unsigned int的end,那么又会发生上面一样的问题
		*/

			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}
			我们可以把pos又强转为int,这样头插就进去了,但是库里面就是unsigned int,模拟实现要和库里的参数类型一致*/

			size_t end = _size+1;
			//这样我等于0的时候就终止了,就没有了以前的问题
			while (end > pos)
			{
				_str[end] = _str[end-1];
				--end;
			}

			_str[pos] = ch;
			_size++;

			return *this;
		}

		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			/*if (len == 0)
			{
				return *this;
			}*/

			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			// 往后挪动len个位置
			size_t end = _size + len;
			while (end > pos+len-1)
			//while (end >= pos + len)怕极端情况pos和len都为0,但是可以通过上面的if进行一个提前判断进行解决
			{
				_str[end] = _str[end -len];
				--end;
			}

			strncpy(_str + pos, str, len);//strcpy遇到0才停止
			_size += len;

			return *this;
		}
		
		void push_back(char ch)
		{
			insert(_size, ch);
		}

		void append(const char* str)
		{
			insert(_size, str);
		}
		
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		
		string& earse(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}

				_size -= len;
			}

			return *this;
		}

		size_t find(char ch, size_t pos = 0)
		{
			for (; pos < _size; ++pos)
			{
				if (_str[pos] == ch)
				{
					return pos;
				}
			}

			return npos;
		}

		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, str);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _str;//返回下标
			}
		}
	private:
		char* _str;
		size_t _size;     // 有效字符个数
		size_t _capacity; // 实际存储有效字符的空间

		//const static size_t npos = -1;
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
		/*out<<s.c_str()<<endl;
		return out;
		这段代码不可行,因为\0打印不出来,这是按C的字符串进行识别的,而C打印字符串遇到0就终止*/
		for (auto ch : s)
		{
			out << ch;
		}

		return out;
	}
	
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
	
	istream& operator>>(istream& in, string& s)
	{
		//方案1
		//char ch;
		in >> ch;
		//cin是识别不了空格或者换行的,你输入多个字符时,它会认为空格或者换行为多个字符之间的间隔,所以它永远不会识别空格或换行
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	//in >> ch;
		//}

		//方案2
		//char ch;
		//ch = in.get();
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	ch = in.get();
		//}

		//return in;
		
		//方案3
		//读满我一次性加到string后面,比多次调用+=效率更高。
		s.clear();//先把已经存在的数据给清掉
		char ch;
		ch = in.get();
		char buff[128] = {'\0'};
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				s += buff;
				memset(buff, '\0', 128);
				i = 0;
			}

			ch = in.get();
		}

		s += buff;//避免string频繁扩容
		return in;
	}

	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

string类的模拟实现 的相关文章

  • 为什么相同的代码在同一台计算机上的执行时间可能不同?

    我是 C 编程新手 我编写了代码并希望获得它的运行时 这就是我所做的 每次运行代码时 我都会得到不同的运行时值 这样对吗 或者我的代码有问题吗 int main int argc char argv time t start end sta
  • 如何在 Visual Studio 2010 中增强 XAML 设计器?

    当我使用 XAML 设计器时 进入设计器和退出设计器是如此困难和缓慢 当我这样做时 Visual Studio 卡了一段时间 有什么方法可以增强 XAML 设计器和编辑器吗 Ant 保存 XAML 文件时非常慢 这通常意味着您可能有复杂的
  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • 如何使用MemoryCache代替Timer来触发一个方法?

    以下方法通过等待已运行操作的结果来处理并发请求 对数据的请求可能会使用相同 不同的凭据同时出现 对于每组唯一的凭据 最多可以有一个GetCurrentInternal呼叫正在进行中 当准备就绪时 该呼叫的结果将返回给所有排队的服务员 pri
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 向 Nhibernate 发出 SQL 查询

    如何将此 SQL 查询发送给 Nhibernate SELECT Customer name FROM Company INNER JOIN Customer ON Company CompanyId Customer CompanyId
  • 将内置类型转换为向量

    我的 TcpClient 类接受vector
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 无法在 Windows 运行时组件库的 UserControl 中创建依赖项属性

    我想在用户控件内创建数据可绑定属性 这个用户控件包含一个 Windows 运行时组件 项目 我使用下面的代码来创建属性 public MyItem CurrentItem get return MyItem GetValue Current
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • ASP.NET:获取自 1970 年 1 月 1 日以来的毫秒数

    我有一个 ASP NET VB NET 日期 我试图获取自 1970 年 1 月 1 日以来的毫秒数 我尝试在 MSDN 中寻找方法 但找不到任何东西 有谁知道如何做到这一点 从 NET 4 6 开始 该方法ToUnixTimeMillis
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 如何在 Blackberry Cascades 中显示具有特定号码的电话板

    我正在使用带有 C QT 和 QML 的 Blackberry Cascades 10 Beta 3 SDK 以及 Blackberry 10 Dev Alpha Simulator 和 QNX Momentics IDE 并且我正在尝试实
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了
  • 如何正确使用 std::condition_variable?

    我很困惑conditions variables以及如何 安全 使用它们 在我的应用程序中 我有一个创建 gui 线程的类 但是当 gui 是由 gui 线程构造时 主线程需要等待 情况与下面的函数相同 主线程创建互斥体 锁和conditi

随机推荐

  • Golang 单元测试

    想要测试Go代码需要依赖go test命令 需注意如下事项 在包目录内所有测试文件必须以 test go结尾 go build不会把这些测试文件编译到最终的可执行文件中 在 test go文件中 单元测试函数的名字须以TestXxxx的形式
  • git: 如何减少.git文件的大小?

    1 起因 使用git储存本地笔记时 没有注意到其中包含的视频文件 avi 大约3个 每个100MB 将其也添加到git的历史记录中 git add git commit 虽然之后删除了视频文件本身 但其提交记录永久的留在了 git中 被gi
  • 自定义封装异步任务组件,实现FutureTask功能

    FutureTask 在 JDK1 8 后的异步编排API中的CompletableFuture 提供了 异步任务的成功回调 异常回调 public class FutureTaskTest public static void main
  • springmvc进阶(5):mvc:default-servlet-handler详解

    我们在配置dispatchServlet时配置
  • MyBatis 万字长文:从入门到动态SQL超详细

    文章目录 1 前言 2 创建项目 3 添加框架支持 4 建库 5 配置数据库连接信息和 XML 文件路径 5 1 创建 Java 类 5 2 Java 接口 5 3 XML 文件 6 查询 6 1 不带参数的查询 6 2 单元测试 6 3
  • 时序预测

    时序预测 MATLAB实现基于梯度训练算法的RBF径向基神经网络时间序列预测 目录 时序预测 MATLAB实现基于梯度训练算法的RBF径向基神经网络时间序列预测 预测效果 模型描述 程序设计 参考资料 预测效果 模型描述 RBF神将网络是一
  • 要多坑有多坑springboot内置定时任务本地可以执行,部署到服务器就不执行了

    使用了springboot的内置定时任务坑1 Scheduled定时任务默认是单线程如果同时执行多个定时任务需加上 可以同时执行多个定时任务 return Bean public TaskScheduler taskScheduler Th
  • MySQL中的is marked as crashed and should be repaired故障

    这个故障不是大问题 不是数据库损坏 只是数据库自己崩了 只需要在cmd中进如存放mysql处的bin文件夹下 输入命令myisamchk c r Data bishe wenxian MYI 该工具就可以自动修补索引 然后 就可以正常用了
  • hyperledger fabric搭建first-network遇到问题

    Hyperledger fabric搭建网络报错问题解决 ubuntu 上搭建hyperledger fabric v1 4 出现问题如下 运行脚本 开启first network案例的网络 sudo byfn sh up 报错 Error
  • 一文看懂什么是HTTPS,及其安全传输机制和原理

    一文看懂什么是HTTPS 及其安全传输机制和原理 一 前言 二 什么是HTTP和HTTPS 1 什么是HTTP 1 1 HTTP的工作原理 1 2 HTTP 的缺点 1 3 HTTP的实际案例 2 HTTPS Hyper Text Tran
  • 浅谈ArrayList动态扩容

    环境 eclipse jdk1 8 简介 ArrayList实现了List接口 继承了AbstractList 底层是数组实现的 一般我们把它认为是可以自增扩容的数组 它是非线程安全的 一般多用于单线程环境下 与Vector最大的区别就是
  • .json格式是什么?如何快速打开.json文件?

    json格式是什么 JSON JavaScript Object Notation 是一种轻量级的数据交换格式 它基于JavaScript语言的一个子集 但是它是独立于编程语言的 可以被多种编程语言使用和解析 JSON格式的数据易于读写和解
  • ubuntu一键安装vnc脚本

    在Ubuntu16 04上测试过 其它机器请自行测试 注意 此脚本默认会重启机器 如果不想重启请注释或删除掉最后一步 脚本名称 ubuntu1604VNC 脚本描述 自动安装配置和自动启动X11Vnc 软件版本 0 1 注意事项 运行完5秒
  • 报gfortran版本太老

    报错gfortran版本太老 可能的原因 需要更新一下 更新成比较新的版本 更新gfortran的时候 需要更新gcc 因为gfortran是gcc的一个编译器 更新gcc可以参考Linux升级gcc到最新版本gcc 11 2 0 Dan淡
  • BES2500Y之开机进TWS配对

    使用场景 刚刚烧录程序的耳机 怎么实现开机自动进TWS配对 case APP POWERON CASE NORMAL if defined BTIF EARPHONE defined EARPHONE STAY BOTH SCAN if d
  • 【cocos creator 3.x】精灵图片不显示

    精灵图片不显示 现象 原因 解决方案 现象 在cocos creator 3 2版本的使用中遇到了精灵图片无法展示的几个场景 在prefab某个node下Sprite的图片无法显示 动态加载prefab时 某些节点的图片无法显示 原因 图片
  • JAVA关键字详解

    JAVA关键字详解 1 final数据 1 gt 对于基本类型前加以final修饰 表示被修饰的变量为常数 不可以修改 一个既是static有是final的字段表示只占据一段不能改变的存储空间 2 gt final用于对象应用时 final
  • tp5 数组进行分页

    首页引入文件 use think paginator driver Bootstrap
  • ES Aggs count distinct group by聚合排序查询

    ES Aggs count distinct group by聚合排序查询 1 kibana query hits限制了10000条 添加 track total hits true query 2 查询返回特定字段 source incl
  • string类的模拟实现

    namespace swx string需要考虑完善的增删查改 class string public typedef char iterator typedef const char const iterator const iterat