【C++模拟实现】string的模拟实现

news2024/11/28 3:38:43

【C++模拟实现】string的模拟实现

目录

  • 【C++模拟实现】string的模拟实现
      • string模拟实现的标准代码
      • string模拟实现中的要点
          • string构造函数的实现
          • 赋值运算符重载
          • 迭代器的实现
          • 对流插入和流提取运算符的重载
          • find函数的实现
          • insert函数的实现

作者:爱写代码的刚子
时间:2023.7.22
前言:本篇博客主要介绍string的模拟实现,之后会更新一系列STL中一些重要容器的模拟实现。

string模拟实现的标准代码

#pragma once

#include<assert.h>

namespace string_imitate
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

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

		/*string(const char* str)
			:_size(strlen(str))
			,_capacity(_size)
			, _str(new char[_capacity + 1])
		{
			strcpy(_str, str);
		}*/

		/*string()
			:_size(0)
			,_capacity(0)
			,_str(new char[1])
		{
			_str[0] = '\0';
		}*/

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

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

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

				_size = s._size;
				_capacity = s._capacity;
			}

			return *this;
		}*/

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

		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);

		//		//this->swap(tmp);
		//		swap(tmp);
		//	}

		//	return *this;
		//}

		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

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

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

		size_t size() const
		{
			return _size;
		}

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

			return _str[pos];
		}

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

			return _str[pos];
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				cout << "reserve()->" << n << endl;

				char* tmp = new char[n + 1];
				//strcpy(tmp, _str);
				memcpy(tmp, _str, _size+1);

				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

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

				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

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

			_str[_size] = ch;

			++_size;
			_str[_size] = '\0';
		}

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

			//strcpy(_str + _size, str);
			memcpy(_str + _size, str, len+1);

			_size += len;
		}

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

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

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

			if (_size +n > _capacity)
			{
				// 至少扩容到_size + len
				reserve(_size + n);
			}

			// 挪动数据
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + n] = _str[end];
				--end;
			}*/


			// 添加注释最好
			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				--end;
			}

			for (size_t i = 0; i < n; i++)
			{
				_str[pos + i] = ch;
			}

			_size += n;
		}

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

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				// 至少扩容到_size + len
				reserve(_size + len);
			}

			// 添加注释最好
			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			for (size_t i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}

			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos <= _size);

			if (len == npos || pos + len >= _size)
			{
				//_str[pos] = '\0';
				_size = pos;

				_str[_size] = '\0';
			}
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

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

			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

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

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

		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);

			size_t n = len;
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}

			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; i++)
			{
				tmp += _str[i];
			}

			return tmp;
		}

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

		//bool operator<(const string& s)
		//{
		//	size_t i1 = 0;
		//	size_t i2 = 0;
		//	while (i1 < _size && i2 < s._size)
		//	{
		//		if (_str[i1] < s._str[i2])
		//		{
		//			return true;
		//		}
		//		else if (_str[i1] > s._str[i2])
		//		{
		//			return false;
		//		}
		//		else
		//		{
		//			++i1;
		//			++i2;
		//		}
		//	}

		//	/*if (i1 == _size && i2 != s._size)
		//	{
		//		return true;
		//	}
		//	else
		//	{
		//		return false;
		//	}*/

		//	//return i1 == _size && i2 != s._size;
		//	return _size < s._size;
		//}

		bool operator<(const string& s) const
		{
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			
			// "hello" "hello"   false
			// "helloxx" "hello" false
			// "hello" "helloxx" true
			return ret == 0 ? _size < s._size : ret < 0;
		}

		bool operator==(const string& s) const
		{
			return _size == s._size 
				&& memcmp(_str, s._str, _size) == 0;
		}

		bool operator<=(const string& s) const
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s) const
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s) const
		{
			return !(*this < s);
		}

		bool operator!=(const string& s) const
		{
			return !(*this == s);
		}

	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	
	public:
		//const static size_t npos = -1; // 虽然可以这样用,但是不建议
		const static size_t npos;

		//const static double x;
	};

	const size_t string::npos = -1;
	//const double string::x = 1.1;


	ostream& operator<<(ostream& out, const string& s)
	{
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/

		for (auto ch : s)
		{
			out << ch;
		}

		return out;
	}

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

		char ch = in.get();
		// 处理前缓冲区前面的空格或者换行
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}

		//in >> ch;
		char buff[128];
		int i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//in >> ch;
			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
};

对于string的模拟实现,我们需要多敲代码,进一步熟悉string。作者也敲了好多次,但给出的是标准的代码不是作者敲的。

string模拟实现中的要点

string构造函数的实现
  • 拷贝数据的选择strcpy还是memcpy?
    一般来说,两者并没有太大的区别,但是strcpy在底层中拷贝到‘/0’就结束了,而memcpy则是内存的拷贝。但是当我们实现对运算符+=进行重载的时候会发现,如果遇到字符中含有‘/0’,如:“1234/01234”,如果使用strcpy并不会将字符串完全拷贝,所以我们选择用memcpy进行拷贝。
  • 默认构造较好的实现(给缺省值)
string(const char* str = "")
                :_capacity(strlen(str))
                ,_size(strlen(str))
        {
            _str=new char[_size+1];
            memcpy(_str,str,_size+1);
        }
赋值运算符重载
  • operator的现代写法:
    1.传统写法:
 string& operator=(const string &s)
        {
            _capacity=s._capacity;
            _size=s._size;
            _str=new char[_size+1];
            memcpy(_str,s._str,s._size+1);
            return *this;
        }

2.现代写法:

string& operator=(const string &s)
        {
            string tmp(s);
            return *this;
        }
void swap(string& s)
        {
            if(this!=&s)
            {
                string tmp(s);
                std::swap(_str,s._str);
                std::swap(_capacity,s._capacity);
                std::swap(_size,s._size);
            }
        }

这种写法在很多场景下都可以借鉴。

迭代器的实现
  • 如何区分const迭代器和非const迭代器?
  • 先用typedef定义两种类型的迭代器:
typedef char* iterator;
typedef const char* const_iterator;

普通迭代器:

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

const迭代器:

  const_iterator begin() const
    {
        return _str;
    }

    const_iterator end() const
    {
        return _str+_size;
    }
对流插入和流提取运算符的重载
  • 前提:
    流插入和流提取的函数实现必须在类外,不然在类里面定义的函数使用时会不符合习惯,所以我们将流插入和流提取函数作为string类的友元函数。
  • 对<<的重载
ostream& operator<<(ostream& _out, const bit::string& s)
    {
        for(auto ch : s)
        {
            _out<<ch;
        }
        return _out;
    }
  1. 注意以上代码中变量_out的类型必须为ostream&,返回值必须为ostream&(必须加上引用),否则不能实现连续的流提取
  2. 对于参数s要加上const,否则传入的const参数将无法使用。
  • 对>>的重载
    错误写法:
istream& operator>>(istream& _cin, bit::string& s)
    {
        char ch;
        _cin>>ch;
        while(ch!=' '&&ch!='\n')
        {
            s+=ch;
            _cin>>ch;
        }
        return _cin;
    }

这种写法遇见空格缓冲字符时读取就会停止,并不能很好地实现流提取。

 istream& operator>>(istream& _cin, bit::string& s)
    {
        char ch=_cin.get();
        while(ch!=' '&&ch!='\n')
        {
            s+=ch;
            ch=_cin.get();
        }
        return _cin;
    }

cin里面提供了get()函数,遇见空格并不会停止读取。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

find函数的实现

可以利用C语言库中的strstr实现。
还有一个易忘点:指针-指针的绝对值等于指针对应数据之间的偏移量。

 size_t find (const char* s, size_t pos = 0) const
        {
            assert(pos<_size);
            const char*ptr=std::strstr(_str+pos,s);
            if(ptr)
            {
                return ptr-_str;
            }
            else
            {
                return npos;
            }
        }
insert函数的实现

注意以下代码中end的变化,以及while语句中的条件:

string& 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;
            while(end>=pos&&end!=npos)
            {
                _str[end+len]=_str[end];
                --end;
            }
            for(int i=0;i<len;i++)
            {
                _str[pos+i]=str[i];
            }
            _size+=len;
            return *this;
        }
  • 如果我们只考虑到while(end>=pos)那insert的实现就错了。因为pos的类型是size_t,所以pos只能为正数,当插入的位置为0时,end将会为-1,end始终大于0,所以陷入循环中。
  • 我们考虑用npos常变量来解决此问题,于是有了循环条件:while(end>=pos&&end!=npos)。
    如何定义npos变量?
  • 可以在string类中设置一个public变量:const static size_t npos; 在类外进行初始化:const size_t string::npos = -1;也可以直接:const static size_t npos = -1;

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

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

相关文章

MySQL视图概念及作用、操作语法

1.什么是视图 在数据库中有一些用户的敏感数据字段不方便展示&#xff0c;需要隐藏时&#xff0c;这时候就可以利用视图这个概念来实现。 2.视图操作 如何对视图里的数据进行增删改操作呢&#xff1f; 可以直接通过insert语句向视图里面插入数据&#xff0c;语法和向表里插…

移动测试(二)

功能测试点 用户使用习惯 权限问题 硬件问题 比如双卡双待、摄像头、GPU等。 操作习惯 用户常用的有菜单键、Home键、返回键、Home键长按&#xff08;显示当前进程列表&#xff09;、调整音量、待机等。相应的作为测试工程师我们需要考虑的项就变成了&#xff1a; • 应用中的…

Docker——基本管理

Docker 基本管理 Docker——基本管理 一、Docker 概述1.Docker的设计理念2.容器的优势3.Docker与虚拟机的区别4.容器在内核中支持2种重要技术5.Docker核心概念5.1 镜像5.2 容器5.3 仓库 二、安装 Docker1.关机防火墙2.安装依赖包3.设置阿里云镜像源4.安装 Docker-CE并设置为开…

PostgreSQL 的事务管理和并发控制机制解析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Istio Pilot源码学习(三):xDS的异步分发

本文基于Istio 1.18.0版本进行源码学习 5、xDS的异步分发 DiscoveryService主要包含下述逻辑&#xff1a; 启动GRPC Server并接收来自Envoy端的连接请求接收Envoy端的xDS请求&#xff0c;从ConfigController和ServiceController中获取配置和服务信息&#xff0c;生成响应消息…

使用 ChatGPT 碰到的坑

最近在使用 ChatGPT 的时候碰到一个小坑&#xff0c;因为某些特殊情况我需要使用 syslog 向 logbeat 中发送日志。 由于这是一个比较古老的协议&#xff0c;确实也没接触过&#xff0c;所以就想着让 ChatGPT 帮我生成个例子。 原本我已经在 Go 中将这个流程跑通&#xff0c;所…

快速排序qsort讲解

hello大家好&#xff0c;我是c语言boom家宝&#xff0c;今天为大家分享的博客内容是qsort快速排序&#xff0c;简称快排的一个知识点的讲解。 在讲到快排之前&#xff0c;允许博主先提一嘴冒泡排序。大家在c语言的学习过程中&#xff0c;冒泡排序是必不可少会学习到的一个思想&…

Kafka - Primie Number of Partitions Issue Consumer Group Rebalance

文章目录 生产者&#xff1a;将数据写入 Kafka 的客户端。 消费者&#xff1a;从 Kafka 中读取数据的客户端。 Topic&#xff1a;Kafka 中用于组织和存储数据的逻辑概念&#xff0c;类似于数据库表。 Record&#xff1a;发送到 Topic 的消息称为 Record。 Partition&#x…

基于深度学习的高精度交通信号灯检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度交通信号灯检测识别可用于日常生活中检测与定位交通信号灯目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的交通信号灯目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检…

斯坦福数据挖掘教程·第三版》读书笔记(英文版)Chapter 13 Neural Nets and Deep Learning

来源&#xff1a;《斯坦福数据挖掘教程第三版》对应的公开英文书和PPT Chapter 13 Neural Nets and Deep Learning In this chapter, we shall consider the design of neural nets, which are collections of perceptrons, or nodes, where the outputs of one rank (or lay…

C# 学习笔记

不再是学生了&#xff0c;成了社畜了&#xff0c;公司主要技术栈是C# 大一时候学C#学的很迷糊&#xff0c;总要重新学一下 入职已经20天了&#xff0c;也开始上手简单增删改查了 记录了一些C#相关的东西&#xff0c;只是还没有系统整理 WinForm 控件命名规范 ADO.NET 连接…

爬虫-微博个人主页的获取

我们在利用爬虫爬取微博个人主页的时候&#xff0c;我们需要获取到个人页面的cookie才能进入到微博的个人主页&#xff0c;否则的话将会是一直跳转到登录页面而导致不能进入个人主页。 import urllib.request url #自己微博个人主页的源代码 headers {User-Agent:Mozilla/5.…

办公软件ppt的制作

毕业找工作太难了&#xff0c;赶紧多学点什么东西吧&#xff0c;今天开始办公软件ppt的制作学习。 本文以WPS作为默认办公软件&#xff0c;问为什么不是PowerPoint&#xff0c;问就是没钱买不起&#xff0c;绝对不是不会破解的原因。 一.认识软件 在快捷工具栏中顾名思义就是一…

什么是框架?为什么要学框架?

一、什么是框架 框架是整个或部分应用的可重用设计&#xff0c;是可定制化的应用骨架。它可以帮开发人员简化开发过程&#xff0c;提高开发效率。 项目里有一部分代码&#xff1a;和业务无关&#xff0c;而又不得不写的代码>框架 项目里剩下的部分代码&#xff1a;实现业务…

机器学习:Bert and its family

Bert 先用无监督的语料去训练通用模型&#xff0c;然后再针对小任务进行专项训练学习。 ELMoBertERNIEGroverBert&PALS Outline Pre-train Model 首先介绍预训练模型&#xff0c;预训练模型的作用是将一些token表示成一个vector 比如&#xff1a; Word2vecGlove 但是对于…

Qt Creator创建控制台项目显示中文乱码

今天在使用Qt Creator创建c项目的时候显示中文乱码&#xff0c;这里分享一下解决办法&#xff0c;主要是由于我们的电脑大部分是GBK编码格式的是&#xff0c;然后Qt默认创建的一般是utf-8编码类型的。编码类型不一致就会导致中文乱码的现象。 从控制台的属性可以看到我们的程序…

Observability:Synthetic monitoring - 动手实践

在我之前的如下文章里&#xff1a; Observability&#xff1a;Synthetic monitoring - 合成监测入门&#xff08;一&#xff09;&#xff08;二&#xff09; Observability&#xff1a;Synthetic monitoring - 创建浏览器监测&#xff0c;配置单独的浏览器监测器及项目 我详…

基于RASC的keil电子时钟制作(瑞萨RA)(3)----使用J-Link烧写程序到瑞萨芯片

基于RASC的keil电子时钟制作3_使用J-Link烧写程序到瑞萨芯片 概述硬件准备视频教程软件准备hex文件准备J-Link与瑞萨开发板进行SWD方式接线烧录 概述 这一节主要讲解如何使用J-Link对瑞萨RA芯片进行烧录。 硬件准备 首先需要准备一个开发板&#xff0c;这里我准备的是芯片型…

【Node.js 安装】Node.js安装与使用教程

Node.js 安装 Node.js 是什么那什么是运行时 如何安装 Node.jsNode 使用教程 Node.js 是什么 先说结论&#xff0c;Node.js 它是一套 JavaScript 运行环境&#xff0c;用来支持 JavaScript 代码的执行 JavaScript 诞生于 1995 年&#xff0c;几乎是和互联网同时出现&#xf…

leetcode-206.反转链表

leetcode-206.反转链表 文章目录 leetcode-206.反转链表一.题目描述二.代码提交三.易错点 一.题目描述 二.代码提交 代码 class Solution {public:ListNode *reverseList(ListNode *head) {ListNode *temp; // 保存cur的下一个节点ListNode *cur head;ListNode *pre nullptr…