C++ string的模拟实现

news2025/3/22 7:29:53

Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!!

一、string的框架搭建

1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需要一个文件来实现方法;

包一下头文件ok初步工作完成;

1.2明确string需要用到的成员变量以及成员函数

成员变量:

①必不可少的一个指向字符串的char*指针;(注意这里不能设置为const char*)

②字符串存储的有效数据size_t size;(有效数据指的时'\0'之前的数据)

③字符串存储的那块空间的容量size_t capacity;

④一个无符号最大值npos(用来判断);

成员函数:

①string的构造函数;包括用字符串构造、用string对象构造、用n个字符构造、用迭代器区间构造;实现常用的几个即可;

②string的析构函数;(因为涉及深拷贝所以要写)

③string的运算符重载函数;包括赋值运算符重载,比较小于、大于、等于、不等于运算重载函数,还有下标访问的运算符重载函数,流插入流提取运算符重载函数;

普通成员函数:

①开空间并初始化的 resize函数;

②开空间不初始化的reserve函数;

③插入单个字符的push_back函数;

④插入字符串的+=运算符重载函数;

⑤某个位置插入n个字符的insert函数;

⑥某个位置删除n个字符的erase函数;

⑦获取string有效数据的size()函数;

⑧获取string存储空间大小的capacity()函数;

⑨清理字符串的clear函数;

⑩从某个位置开始查找某个字符的find函数;

11、从某个位置开始截取长度为len的字符串;

12、获取_str的c_str函数;

还有一个需要用到的是迭代器;

1.3声明到头文件

由于模拟实现的string类的名字会跟std里的string冲突,所以需要namespace一个命名空间;

#pragma once
#include <iostream>
using namespace std;
//string的模拟实现
namespace ldc
{
	struct string
	{
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		string(const char* str);
		string(const string& s);
		string(size_t n, char c);
		string(const_iterator it1, const_iterator it2);
		~string();
		string& operator=(const string& s);
		bool operator>(const string& s)const;
        bool operator<(const string& s)const;
        bool operator==(const string& s)const;
        bool operator!=(const string& s)const;
        bool operator >= (const string & s)const;
        bool operator <=(const string& s)const;
		char operator[](size_t pos);
		const char operator[](size_t pos)const;
		void resize(size_t n, char c='\0');
		void reserve(size_t n);
		void push_back(char c);
		string& operator+=(const char* str);
		string& opeartor += (const string & s);
        string& ldc::string::operator+=(char ch);

		void insert(size_t pos, size_t n, char c);
		void erase(size_t pos, size_t n=npos);
		size_t size();
		size_t capacity();
		void clear();
		size_t find(char c, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		string& substr(size_t pos = 0, size_t len = npos);
        const char* c_str const();
	private:
		 char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos=-1;
	};
    ostream& operator<<(ostream& out, const string& s);//流提出插入重载
	istream& operator>>(istream& in, string& s);//在类外重载不存在this
}

这里注意:npos用来做判断,当调用erase在pos位置删除n个字符时,如果不传参n默认为npos(无符号最大值),表示从pos位置开始往后的全删除;(同理substr也一样);

二、开始实现

2.1构造函数的实现
 #define _CRT_SECURE_NO_WARNINGS 1
#include "string3_20.h"
#include <string.h>
//string的方法实现
//构造函数
ldc::string::string(const char* str="")
{
	size_t len = strlen(str);
	_size = len;
	_capacity = _size;
	_str = new char[_capacity + 1];//这里+1因为要保留一个位置存放'\0',空串也有'\0'
	memcpy(_str, str, len + 1);
}
ldc::string::string(const string& s)
{
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	memcpy(_str, s._str, _capacity + 1);
}
ldc::string::string(size_t n, char c)
{
	_size = n;
	_capacity = _size;
	_str = new char[_capacity + 1];
	for (size_t i = 0; i < n; i++)
	{
		_str[i] = c;
	}
	_str[n] = '\0';
}
ldc::string::string(const_iterator it1, const_iterator it2)
{
	_size = it2 - it1;
	_capacity = _size;
	_str = new char[_capacity + 1];
	memcpy(_str, it1, _size);
	_str[_size] = '\0';
}

顺便把流插入重载和c_str也实现了(好测试)

const char* ldc::string::c_str()const
{
	return _str;
}
ostream& ldc::operator<<(ostream& out, const string& s)
{
	
	out <<s.c_str();
	return out;
}

测试:

 #define _CRT_SECURE_NO_WARNINGS 1
//string的测试
#include "string3_20.h"
int main()
{
	ldc::string s1("hello world!!"); //字符串构造
	const char* ch = "welcome";  
	ldc::string s2(ch);
	cout << s1 << endl;
	cout << s2 << endl;
    char ch2[10] = { "yes!!!" };
    ldc::string s3(ch2, ch2 + 3);//区间构造
    cout << s3 << endl;
    ldc::string s4(s3);//拷贝构造
    cout<<s4<<endl;
	return 0;
}

ok没问题!接下来实现以下赋值重载跟析构;

2.2赋值重载与析构函数
ldc::string& ldc::string::operator=(const string& s)
{
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	memcpy(_str, s._str, _capacity + 1);
     return *this;
}
ldc::string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

测试:

int main()
{
	ldc::string s1("hello");
	ldc::string s2("welcome");
	s1 = s2;
	cout << s1 << endl;
	return 0;
}

没问题,接下来实现普通成员函数;

2.3普通成员函数的实现
//比较、下标访问运算符重载
bool ldc::string::operator>(const string& s)const
{
	//比较有很多种方法,可以用operator[]比也可以获取str后再比,这里用最简洁的方法
	//库里的memcmp内存比较函数
	int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//先比短
	return ret == 0 ? _size > s._size:ret > 0;//再比长,如果相等ret为正数表示str1>str2
}
bool ldc::string::operator<=(const string& s)const
{
	return !(*this > s);
}
bool ldc::string::operator<(const string& s)const
{
	return !(*this >= s);
}
bool ldc::string::operator>=(const string& s)const
{
	return (*this > s) || (*this == s);
}
bool ldc::string::operator==(const string& s)const
{
	return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool ldc::string::operator!=(const string& s)const
{
	return !(*this == s);
}

char ldc::string::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
const char ldc::string::operator[](size_t pos)const
{
	assert(pos < _size);
	return _str[pos];
}

测试:

void test1()
{
	ldc::string s1("hello");
	ldc::string s2("helloxxx");
	ldc::string s3("welcome");
	ldc::string s4("welcome");
	cout << (s1 > s2) << " ";
	cout << (s1 >= s2) << " ";
	cout << (s1 < s2) << " ";
	cout << (s1 <= s2) << " ";
	cout << (s3 == s4) << " ";
	cout << (s3!= s2) << " ";
	cout << s1[2] << " ";
	s1[0]='Y';
	cout << s1 << " ";
	//const ldc::string s5("beutiful!");
	//s5[0] = 'B';
}
int main()
{
	test1();
	return 0;
}

OK没问题接着实现扩容:

2.4扩容实现
//只开空间不初始化
void ldc::string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//假设开100个空间,需要存放100个数据,第101个位置需存放结束标识符'\0'
		memcpy(tmp, _str, _size + 1);//连'\0'一起拷贝,不然开完空间后的数据没有结束标志符
		delete[] _str;//释放旧空间
		_str = tmp;//指向新空间
		_capacity = n;//注意只需要修改_capacity,_size有效数据始终没变
	}
}

//开空间并初始化
void ldc::string::resize(size_t n, char c = '\0')
{
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		reserve(n);
		for (size_t i=_size;i<n;i++)
		{
			_str[i] = c;
		}
		_str[n] = '\0';
		_size = n;
	}
}

测试:

void test2()
{
	ldc::string s1("hello");
	s1.resize(20, 'x');
	cout << s1 << endl;
	s1.resize(3);
	cout << s1 << endl;
}
int main()
{
	//test1();
	test2();
	return 0;
}

ok没问题下一步,reserve后面测;

2.5增删查改实现
void ldc::string::push_back(char c)
{
	if (_size == _capacity)
	{
		size_t n = _capacity == 0 ? 4 : _capacity * 2;//二倍扩容
		reserve(n);
	}
	_str[_size] = c;
	++_size;
	_str[_size] = '\0';
}
ldc::string& ldc::string::operator+=(const char* str)
{
	size_t len = strlen(str);
	reserve(_size + len);//复用reserve开空间
	memcpy(_str + _size, str, len + 1);//'\0'一起拷贝过去
	_size += len;
	return *this;
}
ldc::string& ldc::string::operator+=(char ch)
{
	push_back(ch);
	return *this;
}
ldc::string&ldc::string::operator+= (const string & s)
{
	reserve(_size + s._size);
	memcpy(_str + _size, s._str, s._size + 1);
	_size += s._size;
	return *this;
}
void ldc::string::insert(size_t pos, size_t n, char c)
{
	assert(pos <= _size);
	reserve(_size + n);
	if (pos==_size)//尾插
	{
		while (n--)
		{
			push_back(c);
		}
	}
	else
	{
		char* begin = _str + _size;
		char* newbegin = _str + pos;
		char* end = _str + _size + n;
		while (begin>=newbegin)
		{
			*end = *begin;
			--end;
			--begin;
		}
		int i = n;
		while (i--)
		{
			_str[pos] = c;
			++pos;
		}
		_size += n;
	}
	
}
void ldc::string::erase(size_t pos, size_t n)
{
	assert(pos < _size);
	if (n == npos || pos + n > _size)
	{
		_str[pos] = '\0';
	}
	else
	{
		size_t end = _size;
		size_t begin = pos + n;
		while (begin != end)
		{
			_str[pos] = _str[begin];
			++pos; ++begin;
		}
	}
	_size -= n;
	_str[_size] = '\0';
}
size_t ldc::string::find(char c, size_t pos)
{
	assert(pos < _size);
	while (pos != _size)
	{
		if (_str[pos] == c)
		{
			return pos;
		}
		++pos;
	}
	return npos;
}
size_t ldc::string::find(const char* str, size_t pos)
{
	//直接用strstr函数
	char* ptr = strstr(_str + pos, str);
	if (ptr)
	{
		return ptr - _str;
	}
	else
		return npos;
}

测试:

void test3()
{
	ldc::string s1("hello world!!");
	s1.insert(5, 3, 'X');
	cout << s1 << endl;
	s1.insert(0, 3, 'Y');
	cout << s1 << endl;
	s1.insert(19, 2, 'Z');
	cout << s1 << endl;
	s1.erase(0, 3);
	cout << s1 << endl;
	s1.erase(5, 4);
	cout << s1 << endl;
	s1.erase(10, -1);
	cout << s1 << endl;

	cout << s1.find('h')<<endl;
	cout << s1.find("world") << endl;
	cout << s1.find('l', 3) << endl;
}

没啥问题,接着把剩下的都实现了;

三、剩余完善

3.1substr实现
ldc::string ldc::string::substr(size_t pos, size_t len)
{
	assert(pos < _size);
	size_t n = len;
	if (n == npos || pos + n > _size)
	{
		n = _size - pos;
	}
	string temp;
	temp.reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		temp += _str[pos + i];
	}
	return temp;
}
3.2其他实现
size_t ldc::string::size()const
{
	return _size;
}
size_t ldc::string::capacity()const
{
	return _capacity;
}
void ldc::string::clear()
{
	_str[0] = '0';
	_size = 0;
}
istream& ldc::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;
}
3.3测试
void test4()
{
	ldc::string s1("hello");
	ldc::string s2(s1.substr(0, 2));
	cout << s1 << endl;
	cout << s2 << endl;
	cout <<"开空间前:" << s2.capacity() <<" ," << s2.size() << endl;
	s2.reserve(40);
	cout <<"开空间后:"<< s2.size() << ", " << s2.capacity() << endl;
	cin>>s2;
	cout << s2<<endl;
	cout << "流插入后:" << s2.size() << " " << s2.capacity() << endl;
}

OK!!到这里string的基本功能已经实现了!!如果您觉得有所收获,记得点赞收藏+关注哦!!谢谢!!!

咱下期见!!!

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

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

相关文章

使用LangChain实现基于LLM和RAG的PDF问答系统

目录 前言一.大语言模型(LLM)1. 什么是LLM&#xff1f;2. LLM 的能力与特点 二、增强检索生成(RAG)三. 什么是 LangChain&#xff1f;1. LangChain 的核心功能2. LangChain 的优势3. LangChain 的应用场景4. 总结 四.使用 LangChain 实现基于 PDF 的问答系统 前言 本文将介绍 …

C++实现rabbitmq生产者消费者

RabbitMQ是一个开源的消息队列系统&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c; 特点 可靠性&#xff1a;通过持久化、镜像队列等机制保证消息不丢失&#xff0c;确保消息可靠传递。灵活的路由&#xff1a;提供多种路由方式&#xff0c;如…

在VMware上部署【Ubuntu】

镜像下载 国内各镜像站点均可下载Ubuntu镜像&#xff0c;下面例举清华网站 清华镜像站点&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 具体下载步骤如下&#xff1a; 创建虚拟机 准备&#xff1a;在其他空间大的盘中创建存储虚拟机的目录&#xff0c…

又双叒叕Scrapy爬虫相关的面试题及详细解答

Scrapy是Python开发的一个快速、高层次的网络爬虫框架,专注于高效抓取网页并提取结构化数据。其核心设计基于异步处理机制,适合大规模数据采集任务。 文章目录 基础概念1. Scrapy框架的核心组件有哪些?架构与流程2. 描述Scrapy的工作流程核心组件详解3. 如何自定义Item Pipe…

使用STM32CubeMX+DMA+空闲中断实现串口接收和发送数据(STM32G070CBT6)

1.STM32CubeMX配置 &#xff08;1&#xff09;配置SYS &#xff08;2&#xff09;配置RCC &#xff08;3&#xff09;配置串口&#xff0c;此处我用的是串口4&#xff0c;其他串口也是一样的 &#xff08;4&#xff09;配置DMA&#xff0c;将串口4的TX和RX添加到DMA中 &#…

【视觉提示学习】3.21论文随想

. . Frontiers of Information Technology & Electronic Engineering. 2024, 25(1): 42-63 https://doi.org/10.1631/FITEE.2300389 中文综述&#xff0c;根据里面的架构&#xff0c;把视觉提示学习分成两类&#xff0c;一类是单模态提示学习&#xff08;以vit为代表&…

(一)丶Windows安装RabbitMQ可能会遇到的问题

一丶可能会忘了配置ERLang的环境变量 二丶执行命令时报错 第一步 rabbitmq-plugins enable rabbitmq_management 第二部 rabbitmqctl status 三丶修改.erlang.cookie 文件 1.找到C盘目下的.erlang.cookie文件 C:\Users\admin\.erlang.cookie C:\Windows\System32\config\sys…

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…

springboot整合mybatis-plus【详细版】

目录 一&#xff0c;简介 1. 什么是mybatis-plus2.mybatis-plus特点 二&#xff0c;搭建基本环境 1. 导入基本依赖&#xff1a;2. 编写配置文件3. 创建实体类4. 编写controller层5. 编写service接口6. 编写service层7. 编写mapper层 三&#xff0c;基本知识介绍 1. 基本注解 T…

Qt之MVC架构MVD

什么是MVC架构&#xff1a; MVC模式&#xff08;Model–view–controller&#xff09;是软件工程中的一种软件架构模式&#xff0c;把软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controll…

深度解析学术论文成果评估(Artifact Evaluation):从历史到现状

深度解析学术论文成果评估(Artifact Evaluation)&#xff1a;从历史到现状 引言 在计算机科学和工程领域的学术研究中&#xff0c;可重复性和可验证性越来越受到重视。随着实验性研究的复杂性不断增加&#xff0c;确保研究成果可以被其他研究者验证和构建变得尤为重要。这一需…

二分查找上下界问题的思考

背景 最近在做力扣hot100中的二分查找题目时&#xff0c;发现很多题目都用到了二分查找的变种问题&#xff0c;即二分查找上下界问题&#xff0c;例如以下题目&#xff1a; 35. 搜索插入位置 74. 搜索二维矩阵 34. 在排序数组中查找元素的第一个和最后一个位置 它们不同于查找…

关于FastAPI框架的面试题及答案解析

FastAPl是一个现代、快速(高性能)的Web框架,用于构建API,基于Python3.7+的类型提示功能。它由Python开发者SebastianRamirez创建,并且使用了Starlette作为其核心组件以及Pydantic进行数据验证。 文章目录 基础篇1. FastAPI的核心优势是什么?2. 如何定义一个GET请求路由?…

HashMap添加元素的流程图

文章目录 JDK7 vs JDK8 的 HashMap 结构变化Java8 中哈希表的红黑树优化机制HashMap 添加元素的完整流程解析1. 计算 key 的哈希值并确定索引2. 检查该索引位置是否已有元素3. 处理哈希冲突4. 判断当前存储结构&#xff08;链表还是红黑树&#xff09;5. 判断链表长度是否超过 …

面向医药仓储场景下的药品分拣控制策略方法 研究(大纲)

面向医药仓储场景下的药品分拣控制策略方法研究 基于多机器人协同与智能调度的分拣系统设计 第一章 绪论 1.1 研究背景与意义 医药仓储自动化需求&#xff1a; 人工分拣效率低、出错率高&#xff08;如药品批次混淆、过期风险&#xff09;温控药品&#xff08;如疫苗、生物制…

AI大模型介绍

大模型介绍 大模型是指具有大规模参数和复杂计算结构的机器学习模型&#xff0c;通常由深度神经网络构建而成&#xff0c;拥有数十亿甚至数千亿个参数 开发大模型不是从0开始&#xff0c;是建立在已有的大模型基座模型上做开发&#xff0c;构建企业知识库&#xff08;向量数据库…

OpenPCDet详细部署与复现

OpenPCDet简介 OpenPCDet是一个用于3D目标检测的开源工具箱&#xff0c;它提供了多种数据集的加载器&#xff0c;支持多种模型&#xff0c;并且易于扩展。 本人使用硬件与环境 Linux操作系统&#xff08;Ubuntu20.04&#xff09; Python环境&#xff08;Anaconda下独立创建&…

同旺科技USB to I2C 适配器 ---- 指令之间延时功能

所需设备&#xff1a; 内附链接 1、同旺科技USB to I2C 适配器 1、指令之间需要延时发送怎么办&#xff1f;循环过程需要延时怎么办&#xff1f;如何定时发送&#xff1f;现在这些都可以轻松解决&#xff1b; 2、只要在 “发送数据” 栏的Delay单元格里面输入相应的延迟时间就…

网络华为HCIA+HCIP NFV

目录 NFV关键技术&#xff1a;虚拟化 NFV关键技术&#xff1a;云化 NFV架构 NFV标准架构 ​编辑 NFV架构功能模块 NFV架构接口 NFV关键技术&#xff1a;虚拟化 在NFV的道路上&#xff0c;虚拟化是基础&#xff0c;云化是关键。传统电信网络中&#xff0c;各个网元都是…

MySQL0基础学习记录-下载与安装

下载 下载地址&#xff1a; &#xff08;Windows&#xff09;https://dev.mysql.com/downloads/file/?id536787 安装 直接点next&#xff0c;出现&#xff1a; 点execute 然后一直next到这页&#xff1a; next 然后需要给root设置一个密码&#xff1a; 在next。。很多页…