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

前言

string的模拟实现其实就是增删改查,只不过加入了类的概念。

为了防止与std里面的string冲突,所以这里统一用String。

目录

前言

一   初始化和销毁 

1.1  构造函数

1.2  析构函数

 二  迭代器实现

三  容量大小及操作

 四 运算符重载

 4.1  bool operator<(const String& s) const

4.2  bool operator==(const String& s) const 

 4.3  bool operator<=(const String& s) const

4.4 bool operator>(const String& s) const

4.5  bool operator>=(const String& s) const 

 4.6  bool operator!=(const String& s) const

 五  字符串操作 

5.1  截取操作

5.2  查找操作

六  流插入流提取

6.1  ostream& operator<<(ostream& out, const String& s)

6.2   istream& operator>>(istream& in, String& s)

 七  string与string相加

String operator+(const String& s2)

string类模拟实现完整代码

总结


一   初始化和销毁 

1.1  构造函数

对于构造函数来说有有参构造和无参构造

所以直接把他们结合起来

default 
string();
copy 
string (const string& str);

 1.string();//无参构造

2.string (const string& str);//有参构造

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

 如果把_str的初始化放在初始化列表会出问题

private:
    char* _str;
	size_t _size;
	size_t _capacity;
	static const size_t npos = -1;
String(const char* str = "") :_str(new char [_capacity+1]),_size(strlen(str)), _capacity(_size)
	{
		//_str = new char[_capacity+1];
		strcpy(_str, str);
	}

初始化列表是会按照成员变量的顺序去初始化,所以这里 初始化_str,_capacity没有初始化,所以在开空间的时候会出问题,当然你可以换一换位置,但是未免太繁琐,同时这里不能把_str设置为nullptr,如果设置为空,那么_size正初始化就会出问题

1.2  析构函数

 这里的析构函数没有那么多细节,直接释放空间,然后处理其他的成员变量就行了

~String()
	{
		delete[] _str;//注意这里的delete[],不是delete
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}

 二  迭代器实现

其实迭代器可以理解为是指针在进行,有的底层是指针有的是其他的方法,这里我们用指针去模拟实现

//迭代器
	typedef char* iterator;
	typedef 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;
	}

迭代器也需要const类型,这样const类型的函数才能去调用,所以写两份。注意范围for就是无脑替换迭代器,本质和迭代器是一样的。

测试案例

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"
int main()
{
    String str("Test string");
    for (String::iterator it = str.begin(); it != str.end(); ++it)
        cout << *it;
    cout << '\n';
    for (auto ch : str)
    {
        cout << ch << " ";
    }
    cout << endl;
    return 0;
}

还有反向迭代器,这里就不一一列举了,想了解的可以参考string类的介绍

三  容量大小及操作

1.capacity()//表示容量大小

2.size()//有效数据大小

3.max_size()//最大有多少数据

4.empty()//是否为空

5.resize()//扩容

6.reserve()//扩容

size_t size()const
	{
		return _size;
	}
	size_t capacity()const
	{
		return _capacity;
	}
	size_t max_size()const
	{
		return 4294967291;
	}
	bool empty()const 
	{
		return _size == 0;
	}
	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)
		{
			_str[n] = '\n';
			_size = n;
		}
		else
		{
			reserve(n);
			while (_size < n)
			{
				_str[_size] = ch;
				_size++;
			}
			_str[_size] = '\0';
		}
	}

1.对于empty,它是如果为空,才是真,不为空就假

2.对于resize和reserve来说,从参数列表可以看出,resize可以设置初始值,也就是可以改变_size,

但是reserve不行,同时reserve设置的n如果比capacity小的话,是不会造成任何影响或者改变的

3.这里的max_size,这里我设置了一个常量,但是并没有这么简单,因为max_size是根据你当前系统来判断该给多大的,因素很多,但是实现起来很麻烦,这里就简单的设置为初始值了

测试案例

由于其他的测试在之前的string类博客测试过了,所以这里就不一一测试了

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"
int main()
{
	String str("Test string");
	cout << "size: " << str.size() << "\n";
	cout << "capacity: " << str.capacity() << "\n";
	cout << "max_size: " << str.max_size() << "\n";
	return 0;
}

 

 四 运算符重载

 运算符重载就是>,<,=,>=,<=这四种,但是其实写一个大于和等于或者写一个小于和等于就行了,因为其他的都能复用

 4.1  bool operator<(const String& s) const
bool operator<(const String& s) const
	{
		return strcmp(_str, s._str) < 0;
	}

4.2  bool operator==(const String& s) const 
bool operator==(const String& s) const
	{
		return strcmp(_str, s._str) == 0;
	}

由于上面写了<和=的运算符重载,所以下面这几个直接复用前面的东西就行, 注意上面的写法用的是字符串函数进行比较,但是库里面用的是模板,所以这里有出入,如果用模板,就不能这样比较了

 4.3  bool operator<=(const String& s) const
bool operator<=(const String& s) const
	{
		return *this < s || *this == s;
	}
4.4 bool operator>(const String& s) const
bool operator>(const String& s) const
	{
		return !(*this <= s);
	}
4.5  bool operator>=(const String& s) const 
bool operator>=(const String& s) const
	{
		return !(*this < s);
	}
 4.6  bool operator!=(const String& s) const
bool operator!=(const String& s) const
	{
		return !(*this == s);
	}

 五  字符串操作 

5.1  截取操作

String substr(size_t pos = 0, size_t len = npos)const 

String substr(size_t pos = 0, size_t len = npos)const 
	{
		assert(pos >= 0 && pos < _size);
		size_t end = len + pos;//最后的位置
		String s = "";
		if (len == npos || pos + len > _size)//如果长度已经大于当前字符串长度
		{
			len = _size - pos;//新长度就等于pos到_size这么长
			end = _size;//
		}
		s.reserse(len);//开辟空间
		for (int i = pos; i < end; i++)//从pos开始到end结束
		{
			s += _str[i];
		}
		return s;
	}

 测试样例:

5.2  查找操作

size_t find(char c, size_t pos = 0)const

size_t find(char c, size_t pos = 0)const
	{
		for (int i = pos;i < _size; i++)
		{
			if (_str[i] = c)
			{
				return i;
			}
		}
		return npos;
	}

查找一个字符 之间从pos位置开始遍历就行了

size_t find(const char* s, size_t pos = 0)const

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

查找一个字符直接用库函数strstr就行 

测试用例: 

 

 

六  流插入流提取

由于这里的流插入和流提取不会涉及到私有的成员变量,所以不用写成友员函数

6.1  ostream& operator<<(ostream& out, const String& s)
ostream& operator<<(ostream& out, const String& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}
6.2   istream& operator>>(istream& in, String& s)
//流提取
istream& operator>> (istream& in, string& s)
{
    s.clear();
    char ch = in.get();
    while (ch != ' ' && ch != '\n')
    {
        s += ch;
        ch = in.get();
    }

    return in;
}

对于上面这段代码来说,我们首先要用一个clear去清理一下,因为不清理会导致之前的数据存在。

还有一点就是这段代码并不好,因为读字符的时候可能会导致频繁的扩容,我们电脑上面的程序可不止一个,不能一直中断其他程序,来进行这个,这样对于计算机的消耗有点大

istream& operator>>(istream& in, String& s)
{
	char buff[129];
	size_t i = 0;
	char ch;
	ch = in.get();
	while (ch != ' ' && ch != '\0')
	{
		buff[i++] = ch;
		if (i == 128)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

这段代码就是对之前的一个改良,设置一个数组去存, 当存到128个字符的时候再一起把它放进字符串里面去,最后还有判断一下如果i!=128的情况即可

 七  string与string相加

String operator+(const String& s2)

这里用成员函数来写,库里面用的是非成员函数

String operator+(const String& s2)
	{
		String ret;
		ret._size = _size + s2._size;
		ret._str = new char[_capacity + s2._capacity];
		strcpy(ret._str, _str);
		strcpy(ret._str + _size, s2._str);
		return ret;
	}

先开空间,然后把两个字符串放进去就行。

string类模拟实现完整代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class String
{
public:
	
	//迭代器
	typedef char* iterator;
	typedef 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()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}
	//拷贝构造
	String(const String& s):_str(nullptr),_size(s._size), _capacity(s._capacity)
	{
		_str = new char[_capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	//下表访问
	char& operator[](size_t pos)
	{
		assert(pos < _size||pos>=0);
		return _str[pos];
	}
	void swap(String& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	//赋值运算符重载
	String&operator=(String tmp)
	{
		swap(tmp);
		return *this;
	}
	//Capacity
	size_t size()const
	{
		return _size;
	}
	size_t capacity()const
	{
		return _capacity;
	}
	size_t max_size()const
	{
		return 4294967291;
	}
	bool empty()const 
	{
		return _size == 0;
	}
	void reserse(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)
		{
			_str[n] = '\n';
			_size = n;
		}
		else
		{
			reserse(n);
			while (_size < n)
			{
				_str[_size] = ch;
				_size++;
			}
			_str[_size] = '\0';
		}
	}
	//Element access
	char& back()
	{
		return _str[_size - 1];
	}
	const char& back()const
	{
		return _str[_size - 1];
	}
	char& front()
	{
		return _str[0];
	}
	const char& front()const 
	{
		return _str[0];
	}
	//Modifiers
	void append(const char* str)
	{
		size_t n = _size + strlen(str);
		if (n > _capacity)
		{
			reserse(n);
			_capacity = n;
		}
		strcat(_str, str);
		_size += strlen(str);
	}
	void push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserse(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';
	}
	String& operator+=(const String& s)
	{
		append(s._str);
		return *this;
	}
	String& operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	
	String& operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	void insert(size_t pos, char ch)
	{
		assert(pos <= _size && pos >= 0);
		if (_size == _capacity)
		{
			reserse(_capacity == 0 ? 4 : _capacity * 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 && pos >= 0);
		int len = strlen(str);
		if (_size + len > _capacity)
		{
			reserse(_size + len);
		}
		size_t end = _size+1;
		while (end > pos)
		{
			_str[end + len] = _str[end-1];
			end--;
		}
		strncpy(_str + pos, str, len);
		_size += len;
	}
	void erase(size_t pos = 0, size_t len = npos)
	{
		assert(pos >= 0 && pos < _size);
		if (len == npos||pos+len>_size)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t end = pos + len;
			while (end <= _size)
			{
				_str[end - len] = _str[end];
				end++;
			}
			_size -= len;
		}
	}
	//String operations:
	const char* c_str()const
	{
		return _str;
	}
	const char* data()const
	{
		return _str;
	}
	size_t find(char c, size_t pos = 0)const
	{
		for (int i = pos;i < _size; i++)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
		return npos;
	}
	size_t find(const char* s, size_t pos = 0)const
	{
		char* p = strstr(_str + pos, s);
		if (p)
		{
			return p - _str;
		}
		else
		{
			return npos;
		}
	}
	String substr(size_t pos = 0, size_t len = npos)const 
	{
		assert(pos >= 0 && pos < _size);
		size_t end = len + pos;
		String s = "";
		if (len == npos || pos + len > _size)
		{
			len = _size - pos;
			end = _size;
		}
		s.reserse(len);
		for (int i = pos; i < end; i++)
		{
			s += _str[i];
		}
		return s;
	}
	String operator+(const String& s2)
	{
		String ret;
		ret._size = _size + s2._size;
		ret._str = new char[_capacity + s2._capacity];
		strcpy(ret._str, _str);
		strcpy(ret._str + _size, s2._str);
		return ret;
	}
	bool operator<(const String& s) const
	{
		return strcmp(_str, s._str) < 0;
	}

	bool operator==(const String& s) const
	{
		return strcmp(_str, s._str) == 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:
	char* _str;
	size_t _size;
	size_t _capacity;
	static const size_t npos = -1;
};
//non_member constants
ostream& operator<<(ostream& out, const String& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}
istream& operator>>(istream& in, String& s)
{
	char buff[129];
	size_t i = 0;
	char ch;
	ch = in.get();
	while (ch != ' ' && ch != '\0')
	{
		buff[i++] = ch;
		if (i == 128)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

总结

以上就是string的全部内容了,💞。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

03-树3 Tree Traversals Again(浙大数据结构PTA习题)

03-树3 Tree Traversals Again 分数 25 作者 陈越 An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, th…

实际测试stm32中断优先级

https://m.weibo.cn/1711020180/5040208380168258

【字典树(前缀树) 哈希映射 后序序列化】1948. 删除系统中的重复文件夹

本文涉及知识点 字典树&#xff08;前缀树) 哈希映射 后序序列化 LeetCode 1948. 删除系统中的重复文件夹 由于一个漏洞&#xff0c;文件系统中存在许多重复文件夹。给你一个二维数组 paths&#xff0c;其中 paths[i] 是一个表示文件系统中第 i 个文件夹的绝对路径的数组。 …

Codeforces Round 949 (Div. 2) (A~C)

1981A - Turtle and Piggy Are Playing a Game 贪心&#xff0c;每次取x 2&#xff0c;求最大分数 // Problem: B. Turtle and an Infinite Sequence // Contest: Codeforces - Codeforces Round 949 (Div. 2) // URL: https://codeforces.com/contest/1981/problem/B // Me…

iOS组件化 方案 实现

iOS组件化 组件化的原因现在流行的组件化方案方案一、url-block &#xff08;基于 URL Router&#xff09;方案二、protocol调用方式解读 方案三、target-action调用方式解读 gitHub代码链接参考 组件化的原因 模块间解耦模块重用提高团队协作开发效率单元测试 当项目App处于…

2024最新群智能优化算法:大甘蔗鼠算法(Greater Cane Rat Algorithm,GCRA)求解23个函数,提供MATLAB代码

一、大甘蔗鼠算法 大甘蔗鼠算法&#xff08;Greater Cane Rat Algorithm&#xff0c;GCRA&#xff09;由Jeffrey O. Agushaka等人于2024年提出&#xff0c;该算法模拟大甘蔗鼠的智能觅食行为。 参考文献 [1]Agushaka J O, Ezugwu A E, Saha A K, et al. Greater Cane Rat Alg…

LAMMPS - 分子动力学模拟器

本文翻译自&#xff1a;https://www.lammps.org/ 文章目录 一、关于 LAMMPS下载作者R&D 100 二、LAMMPS 亮点毛细血管中的血流 一、关于 LAMMPS 官网&#xff1a; https://www.lammps.org/ github &#xff1a;https://github.com/lammps/lammps LAMMPS 分子动力学模拟器…

初识java——javaSE(8)异常

文章目录 一 异常的概念与体系结构1.1 什么是异常&#xff1f;1.2 异常的体系结构&#xff01;1.3 编译时异常与运行时异常与Error编译时异常&#xff1a;异常声明&#xff1a;throws关键字 运行时异常&#xff1a;什么是Error? 二 处理异常2.1 异常的抛出&#xff1a;throw(注…

利用映射算子打印菱形

文章目录 一、利用RDD完成&#xff08;一&#xff09;右半菱形&#xff08;二&#xff09;左半菱形&#xff08;三&#xff09;完整菱形&#xff08;四&#xff09;输出任意大菱形 二、利用Java完成&#xff08;一&#xff09;右半菱形&#xff08;二&#xff09;左半菱形&…

恒压频比开环控制系统Matlab/Simulink仿真分析(SPWM控制方式)

介绍恒压频比的开环控制方法驱动永磁同步电机的转动&#xff0c;首先分析恒压频比的控制原理&#xff0c;然后在Matlab/Simulink中进行永磁同步电机恒压频比开环控制系统的仿真分析&#xff0c;最后将Simulink中的恒压频比控制算法生成代码加载到实际工程中进行工程实现。 一、…

react 表格实现拖拽功能

项目背景 : react ant 单纯实现拖拽确实不难 , 我的需求是根据后台接口返回 , 生成对应的父子表格 , 并只可以拖拽子的位置 , 如图 后台返回的数据结构 (pid为0说明是父 , 子的pid等于父的id , 说明是父的子) 1 , 我先转成了树形结构且自己加上了key (注意 : key一定得是唯一的…

异常(Exception)

捕获异常 public class test {public static void main(String [] args) {int[] arr {1,2,3,4,5};try {System.out.println(arr[10]);}catch (ArrayIndexOutOfBoundsException e) {//索引越界异常System.out.println("索引越界");}System.out.println("看看我是…

测试FaceRecognitionDotNet报错“Error deserializing object of type int”

FaceRecognitionDotNet宣称是最简单的.net人脸识别模块&#xff0c;其内部使用Dlib、DlibDotNet、OpenCVSharp等模块实现人脸识别&#xff0c;网上有不少介绍文章。实际测试过程中&#xff0c;在调用FaceRecognition.Create函数创建FaceRecognition实例对象时&#xff0c;会报如…

AI入门:普通人可以利用AI做什么?休闲时间赚点小钱?(含多种实践案例)

大家好&#xff0c;我是影子&#xff0c;一名AI编程深耕者。 最近&#xff0c;有很多 AI 小白问我&#xff0c;AI到底可以做些什么&#xff1f;对我们普通人能有哪些帮助&#xff1f; 在我看来&#xff0c;对于我们刚接触 AI 的小伙伴而言。我们可以利用 AI 为我们工作提效&…

构建 VPC 并启动 Web 服务器

实验 2&#xff1a;构建 VPC 并启动 Web 服务器 目标 完成本实验后&#xff0c;您可以&#xff1a; 创建 VPC。创建子网。配置安全组。在 VPC 中启动 EC2 实例。任务 1&#xff1a;创建 VPC 在本任务中&#xff0c;您将使用 VPC 向导在单个可用区中创建一个 VPC、一个互联网网关…

神经网络---卷积神经网络CNN

一、从前馈神经网络到CNN 前馈神经网络&#xff08;Feedforward Neural Networks&#xff09;是最基础的神经网络模型&#xff0c;也被称为多层感知机&#xff08;MLP&#xff09;。 它由多个神经元组成&#xff0c;每个神经元与前一层的所有神经元相连&#xff0c;形成一个“…

电子电气SCI期刊,中科院1区TOP,收稿范围广泛

一、期刊名称 IEEE Transactions on Smart Grid 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;工程技术 影响因子&#xff1a;9.6 中科院分区&#xff1a;1区 三、期刊征稿范围 IEEE Transactions on Smart Grid是一本跨学科期刊&#xff0c;旨在传播智…

Gbase 国产数据库

参考&#xff1a;参考&#xff1a; 5分钟学会Linux环境GBase 8t安装和部署 - 光洋山 - twt企业IT交流平台 (talkwithtrend.com)https://www.talkwithtrend.com/Article/197237 视频 GBase 8s快速入门-功能简介与演示-大数据教程-腾讯课堂 (qq.com)https://ke.qq.com/course/…

Qt 插件机制使用及原理

目录 1.引言 2.插件原理 3.插件实现 3.1.定义一个接口集(只有纯虚函数的类) 3.2.实现接口 4.插件的加载 4.1.静态插件 4.1.1.静态插件实现方式 4.1.2.静态插件加载的过程 4.1.3.示例 4.2.动态插件 4.2.1.动态插件的加载过程 5.定位插件 6.插件开发的优势 7.总结…

蓝桥杯高频考点-与日期相关的题目

文章目录 前言1. 如何枚举合法日期1.1 预存每个月的天数1.2 封装一个判断日期是否合法的函数1.3 枚举日期并判断日期是否合法 2. 判断日期是否为回文日期2.1 将日期当作字符串进行处理2.2 将日期当作一个8位数进行处理 3. 给定初始日期&#xff0c;计算经过n天后对应的日期3.1 …