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

news2025/1/21 7:22:45

前言

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的全部内容了,💞。

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

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

相关文章

二叉树的顺序实现-堆

一、什么是堆 在数据结构中&#xff0c;堆&#xff08;Heap&#xff09;是一种特殊的树形数据结构&#xff0c;用数组存储&#xff0c;通常被用来实现优先队列。 堆具有以下特点&#xff1a; 堆是一棵完全二叉树&#xff08;Complete Binary Tree&#xff09;&#xff0c;即…

uni-app的网络请求库封装及使用(同时支持微信小程序)

其实uni-app中内置的uni.request()已经很强大了&#xff0c;简单且好用。为了让其更好用&#xff0c;同时支持拦截器&#xff0c;支持Promise 写法&#xff0c;特对其进行封装。同时支持H5和小程序环境&#xff0c;更好用啦。文中给出使用示例&#xff0c;可以看到使用变得如此…

算法(六)计数排序

文章目录 计数排序技术排序简介算法实现 计数排序 技术排序简介 计数排序是利用数组下标来确定元素的正确位置的。 假定数组有10个整数&#xff0c;取值范围是0~10&#xff0c;可以根据这有限的范围&#xff0c;建立一个长度为11的数组。数组下标从0到10&#xff0c;元素初始…

智慧校园有哪些特征

随着科技的飞速进步&#xff0c;教育领域正经历着一场深刻的变革。智慧校园&#xff0c;作为这场变革的前沿代表&#xff0c;正在逐步重塑我们的教育理念和实践方式。它不仅仅是一个概念&#xff0c;而是一个集成了物联网、大数据、人工智能等先进技术的综合生态系统&#xff0…

Nginx(openresty) 开启目录浏览 以及进行美化配置

1 nginx 安装 可以参考:Nginx(openresty) 通过lua结合Web前端 实现图片&#xff0c;文件&#xff0c;视频等静态资源 访问权限验证&#xff0c;进行鉴权 &#xff0c;提高安全性-CSDN博客 2 开启目录浏览 location /file{alias /data/www/; #指定目录所在路径autoindex on; …

差旅游记|绵阳印象:与其羡慕他人,不如用力活好自己。

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 来绵阳之前同事就问: “雷工&#xff0c;能吃辣嘛&#xff1f;”。 “还行&#xff0c;能吃点辣。” “那你去了四川别说能吃点辣&#xff0c;那边的能吃点比跟你说的能吃点不太一样” 01 你好 今天打车&#xff0c;上…

信息学奥赛初赛天天练-17-阅读理解-浮点数精准输出与海伦公式的巧妙应用

PDF文档公众号回复关键字:20240531 1 2023 CSP-J 阅读程序1 阅读程序&#xff08;程序输入不超过数组成字符串定义的范围&#xff1a;判断题正确填√&#xff0c;错误填&#xff1b;除特殊说明外&#xff0c;判断题1.5分&#xff0c;选择题3分&#xff0c;共计40分&#xff0…

python 如何判断一组数呈上升还是下降趋势

目录 一、引言 二、基本概念 三、判断方法 直接比较法 斜率法 统计检验法 四、方法比较与选择 五、案例分析 六、总结 一、引言 在数据处理和分析的领域中&#xff0c;判断一组数是否呈现上升或下降趋势是一个重要的环节。这不仅能够帮助我们了解数据的基本变化…

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

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

Ai晚班车531

1.中央网信办等三部门&#xff1a;加快推进大模型、生成式人工智能标准研制。 2.中国石油与中国移动、华为、科大讯飞签署合作协议。 3.Opera浏览器与谷歌云合作&#xff0c;接入 Gemini 大模型。 4.谷歌 Gemini 加持Chromebook Plus。 5.英飞凌&#xff1a;开发 8kW和12kW…

5.25.1 用于组织病理学图像分类的深度注意力特征学习

提出了一种基于深度学习的组织病理学图像分类新方法。我们的方法建立在标准卷积神经网络 (CNN) 的基础上,并结合了两个独立的注意力模块,以实现更有效的特征学习。 具体而言,注意力模块沿不同维度推断注意力图,这有助于将 CNN 聚焦于关键图像区域,并突出显示判别性特征通…

Redis 探索之旅(进阶)

目录 今日良言&#xff1a;从不缺乏从头开始的勇气 一、持久化 1、RDB 2、AOF 二、Redis 的事务 三、主从复制 四、哨兵模式 五、集群模式 六、缓存 七、分布式锁 今日良言&#xff1a;从不缺乏从头开始的勇气 一、持久化 持久化就是把数据存储在硬盘上&#xff0c;无…

使用 DuckDuckGo API 实现多种搜索功能

在日常生活中&#xff0c;我经常使用搜索引擎来查找信息&#xff0c;如谷歌和百度。然而&#xff0c;当我想通过 API 来实现这一功能时&#xff0c;会发现这些搜索引擎并没有提供足够的免费 API 服务。如果有这样的免费 API, 就能定时获取“关注实体”的相关内容&#xff0c;并…

高通开发系列 - ubuntu中的docker安装debian镜像

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 概述当前状态Ubuntu中安装dockerDebian镜像Debian容器中操作更改Debian源安装应用程序

推荐:4本易发表的优质SSCI期刊,含期刊官网!

01、Risk Management and Healthcare Policy 开源四区&#xff0c;国人发表占比25%&#xff0c;发表量前三的国家分别是中国、埃塞俄比亚和美国。 该期刊对国人友好&#xff0c;年度发文量400多&#xff0c;影响因子3.6。 主要刊发公共卫生相关的文章。 研究者可以围绕居民…

【第十三节】C++控制台版本坦克大战小游戏

目录 一、游戏简介 1.1 游戏概述 1.2 知识点应用 1.3 实现功能 1.4 开发环境 二、项目设计 2.1 类的设计 2.2 各类功能 三、程序运行截图 3.1 游戏主菜单 3.2 游戏进行中 3.3 双人作战 3.4 编辑地图 一、游戏简介 1.1 游戏概述 本项目是一款基于C语言开发的控制台…

【学习笔记】Windows GDI绘图(九)Graphics详解(上)

文章目录 Graphics 定义创建Graphics对象的方法通过Graphics绘制不同的形状、线条、图像和文字等通过Graphics操作对象坐标 Graphics属性Clip(裁切/绘制区域)ClipBounds获取裁切区域矩形范围CompositiongMode合成方式CompositingQuality渲染质量DpiX和DpiY 水平、垂直分辨率Int…

2024年5月31日 (周五) 叶子游戏新闻

《Granblue Fantasy: Relink》版本更新 新增可操控角色及功能世嘉股份有限公司现已公开《Granblue Fantasy: Relink》&#xff08;以下简称 Relink&#xff09;免费版本更新ver.1.3.1于5月31日&#xff08;周五&#xff09;上线的消息。该作是由Cygames Inc.&#xff08;下称Cy…

SpringSecurity6从入门到实战之Filter过滤器回顾

SpringSecurity6从入门到实战之Filter过滤器回顾 如果没有SpringSecurity这个框架,我们应该通过什么去实现客户端向服务端发送请求时,先检查用户是否登录,登录了才能访问.否则重定向到登录页面 流程图如下 官方文档&#xff1a;https://docs.spring.io/spring-security/referen…

实际测试stm32中断优先级

HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority); void HAL_NVIC_EnableIRQ(IRQn_Type IRQn); void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);第一个函数 HAL_NVIC_SetPriority 是用来设置单个优先级的抢占优先级和响应优先级的值。第二个…