模拟实现string类

news2025/2/24 2:35:36

请坐图片 的图像结果

 

 string类的接口有很多,这里我来梳理一下自己觉得有意思的几个,并模拟实现一下可以凑合用的my_string,话不多说直接开干:

注意事项

为了和库里的string冲突,所以就将自己实现的my_string放在一个命名空间里

namespace cr  

成员变量

private:
		char* _arr;
		size_t _size;
		size_t _capacity;
public:
		const static size_t npos;

和往常一样,static修饰的成员变量要单独定义,毕竟该成员属于整个类

const size_t cr::my_string::npos = -1;

构造函数 

my_string(const char* arr = "");
cr::my_string::my_string(const char* arr)
	:_size(strlen(arr))
	,_capacity(_size)
{
	_arr = new char[_capacity+1];//加一个\0
	strcpy(_arr, arr);
}

为了避免无惨初始化的情况,这里是要给个缺省值的,空字符串也是有一个元素的('\0'),这里的字符指针_arr是不走初始化列表的,因为我们声明时的顺序会影响着初始化列表执行的顺序,所以最好单独初始化。而string类最容易出错的地方就是末尾的'\0',所以在开辟空间时要多开一个存放'\0'

拷贝构造函数

void cr::my_string::swap(my_string& s)
{
	std::swap(_arr, s._arr);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
cr::my_string::my_string(const my_string& tmp)//拷贝构造函数
	:_arr(nullptr)
	,_size(0) 
	,_capacity(0)
{
	my_string s(tmp._arr);//this并没有初始化,没有处理
	swap(s);
}

拷贝构造的形参一定不要忘记引用接收,防止陷入死循环(因为在调用拷贝构造是就已经反生了一次传参,自定义类型数据传参又会调用拷贝构造函数)。这里的拷贝构造函数是有点不一样的,这里调用了构造函数

my_string s(tmp._arr);//tmp对象就是需要拷贝的内容

仅仅通过一个构造函数就实现了深拷贝,后续的swap会将从tmp对象拷贝好的对象s和this进行交换,但是this里的数据并没有进行初始化,所以交换就可能出问题,所以在这之前在初始化列表中将this初始化一下。所以这里相对于平常的拷贝构造就可以少些很多代码(实际交给函数执行了)

赋值运算符 

cr::my_string& cr::my_string::operator=(my_string tmp)
{
	swap(tmp);
	return *this;
}

这里是不是更加不可思议了呢,实际上就是调用了拷贝构造函数而已,传参的时候,tmp就是通过拷贝构造得到的数据,再swap将得到的数据换给this达到目的,所以就完成赋值操作,结束后而且还自动帮你释放tmp这个临时“中间人”的内存。

operator[]

char& operator[](size_t i);
const char& operator[](size_t i) const;
char& cr::my_string::operator[](size_t i)
{
	assert(i < _size);
	return _arr[i];
}
const char& cr::my_string::operator[](size_t i) const
{
	assert(i < _size);
	return _arr[i];
}

 这里库中实现的就是断言,不仅仅是在string中,STL也是断言判断的。而且最好是实现成const和非const两种,一个只可读,一个可读可改,保障了安全性。

迭代器 

typedef char* iterator;
typedef const char* const_iterator;

iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
cr::my_string::iterator cr::my_string::begin()
{
	return _arr;
}

cr::my_string::iterator cr::my_string::end()
{
	return &_arr[_size];//_arr+_size
}

cr::my_string::const_iterator cr::my_string::begin()const
{
	return _arr;
}

cr::my_string::const_iterator cr::my_string::end()const
{
	return &_arr[_size];//_arr+_size
}

这里要注意的是end()函数返回的值可不是指向最后一个有效数据,指向的是最后一个有效数据的下一个(即'\0')同样是实现了两种。

 reserve与resize

这两个成员函数的功能一定要区分开。

void cr::my_string::reserve(size_t n)
{
	if(n>_capacity)//防止外面调用该函数时出错
	{
		_capacity = n;
		char* tmp = new char[_capacity + 1];
		strcpy(tmp, _arr);
		delete[]_arr;
		_arr = tmp;
	}
}

 reserve的功能就是开空间,一般的使用场景是在提前已经知道数据空间大小的情况,改变的是_capacity的值。如果原数据的_capacity比你reserve的_capacity还要大时,是不会进行缩容的,所以reserve就是扩容的作用。

void cr::my_string::resize(int n,char p)
{
	if (n > _size)
	{
		reserve(n);
		for (int i = size(); i < n; i++)
		{
			_arr[i] = p;
		}
		_arr[n] = 0;
	}
	else
	{
		_arr[n] = 0;
	}
	_size = n;//改变_size的值
}

resize的功能也是对数据空间的操作,但是resize不仅仅改变_capacity还改变_size的值。所以resize不仅仅可以扩容还可以缩容,当resize的空间小于原_size时就是缩容,将多的数据都扔掉,那么如果resize的空间大于原_size时,就是扩容,如果没有第二个参数的话,多的空间会给个缺省值‘\0’,否则就给传过来的值。

 就说这么多了,其他的几个常见的成员函数的源码就放在下面了:

源码


void cr::my_string::push_back(char p)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	_arr[_size] = p;
	_size++;
	_arr[_size] = 0;
}

void cr::my_string::append(const char* p)
{
	int len = strlen(p);
	reserve(len + _capacity);

	strcat(_arr, p);//该函数会将\0拷贝进去
	_size += len;
	
}

void cr::my_string::operator+=(const char* p)
{
	append(p);
}
void cr::my_string::operator+=(char p)
{
	push_back(p);
}
void cr::my_string::operator+=(cr::my_string& s)
{
	*this += s._arr;//s自定义类型,执行结束会调用析构函数
}

void cr::my_string::insert(size_t pos, char p)
{
	assert(pos <= _size);
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_arr[end] = _arr[end-1];
		end--;
	}
	_arr[pos] = p;
	_size++;
}
void cr::my_string::insert(size_t pos,const char* p)
{
	assert(pos <= _size);
	int len = strlen(p);
	if (_size+len>_capacity)
	{
		reserve(_capacity+len);
	}
	size_t end = _size + len;
	while (end >= pos + len)
	{
		_arr[end] = _arr[end - len];
		end--;
	}
	for (int i = 0; i < len; i++)
	{
		_arr[pos++] = p[i];
	}
	_size += len;

}

void cr::my_string::erase(size_t pos, size_t len)
{
	if (len == npos || len + pos >= size())
	{
		_arr[pos] = 0;
		_size = pos;
		return;
	}
	for (int i = pos + len; i <= size(); i++)//i最后指向\0
	{
		_arr[pos++] = _arr[i];
	}
	_size -= len;

}

bool cr::my_string::operator<(const my_string& s)const
{
	return  strcmp(this->_arr, s._arr) < 0;
}
bool cr::my_string::operator>(const my_string& s)const
{
	return strcmp(this->_arr, s._arr) > 0;
}
bool cr::my_string::operator==(const my_string& s)const
{
	return !(*this < s) && !(*this > s);
}
bool cr::my_string::operator>=(const my_string& s)const
{
	return *this > s || *this == s;
}
bool cr::my_string::operator<=(const my_string& s)const
{
	return *this < s || *this == s;
}
bool cr::my_string::operator!=(const my_string& s)const
{
	return !(*this == s);
}

ostream& cr::operator<<(ostream& out, const my_string& s)
{
	//for(int i=0;i<s.size();i++)
	//	out << s[i];

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

	return out;
}

void cr::my_string::clear()
{
	_arr[0] = 0;
	_size = 0;
}

istream& cr::operator>>(istream& in, my_string& s)
{	
    //in>>会以空格和换行进行分割,不会读入
	s.clear();
	char tmp[100]; int i = 0;
	char ch;
	ch = in.get();//会读取每一个字符
	while (ch != ' ' && ch != '\n')
	{
		if(i<99)
		{
			tmp[i++] = ch;
		}
		else
		{
			tmp[i++] = 0;
			s += tmp;//可以存起来再插入,少扩几次
			i = 0;
		}
		ch = in.get();
	}
	tmp[i++] = 0;
	s += tmp;

	return in;
}


size_t cr::my_string::find( char p,int pos)
{
	assert(pos < _size);
	for (int i = pos; i < _size; i++)
	{
		if (_arr[i] == p)
			return i;
	}
	return npos;
}
size_t cr::my_string::find(const char* p,int pos)
{
	assert(pos < _size);
	char* q = strstr(_arr + pos, p);
	if (q)
	{
		return q - _arr;
	}
	return npos;
	
}

cr::my_string cr::my_string::substr(size_t pos, size_t len)
{
	my_string s;
	int n;
	if (len == npos || len + pos >= _size)
		n = _size;
	else
		n = len + pos;
	for (int i = pos; i < n; i++)
	{
		s += _arr[i];
	}
	return s;
}

cr::my_string::~my_string()
{
	delete[]_arr;
	_arr = nullptr;
	_size = _capacity = 0;
}

微信个性搞笑可爱又沙雕的表情包 对你好真的是因为你很重要-腾牛个性网

搞笑表情包 的图像结果

 

 

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

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

相关文章

采用pycharm在虚拟环境使用pyinstaller打包python程序

一年多以前&#xff0c;我写过一篇博客描述了如何虚拟环境打包&#xff0c;这一次有所不同&#xff0c;直接用IDE pycharm构成虚拟环境并运行pyinstaller打包 之前的博文&#xff1a; 虚拟环境venu使用pyinstaller打包python程序_伊玛目的门徒的博客-CSDN博客 第一步&#xf…

明天就要去面试软件测试岗了,现在我能怎么做呢?

首先&#xff0c;时间已经不允许你进行大面积的专业复习&#xff0c;所以你应该做好能够立竿见影的准备工作&#xff1a; 1、整理好自己的仪表 先去理个发&#xff0c;让自己看起来精神一点&#xff0c;尤其是男生&#xff0c;整理头发&#xff0c;修修鬓角能够快速让人对自己…

安卓13不再支持PPTP怎么办?新的连接解决方案分享

随着Android 13的发布&#xff0c;我们迎来了一个令人兴奋的新品时刻。然而&#xff0c;对于一些用户而言&#xff0c;这也意味着必须面对一个重要的问题&#xff1a;Android 13不再支持PPTP协议。如果你是一个习惯使用PPTP协议来连接换地址的用户&#xff0c;那么你可能需要重…

机器学习实战——波士顿房价预测

波士顿房价预测 波士顿房地产市场竞争激烈&#xff0c;而你想成为该地区最好的房地产经纪人。为了更好地与同行竞争&#xff0c;你决定运用机器学习的一些基本概念&#xff0c;帮助客户为自己的房产定下最佳售价。幸运的是&#xff0c;你找到了波士顿房价的数据集&#xff0c;…

tabBar的使用

参考Api&#xff1a;全局配置 | 微信开放文档 (qq.com) 1.使用说明 2.使用详情 3.使用案例 在全局配置的app.json中 "tabBar": {"color": "#333","selectedColor": "#d43c33","backgroundColor": "#fff&qu…

【云原生】Kubernetes控制器中StatefulSet的使用

目录 1 什么是 StatefulSet 2 StatefulSet 特点 3 限制 4 使用 StatefulSet 1 搭建 NFS 服务 2 客户端测试 3 使用 statefulset 1 什么是 StatefulSet 官方地址&#xff1a; StatefulSet | Kubernetes StatefulSet 是用来管理有状态应用的工作负载 API 对象。 无状态应…

半年报增幅超预期,但汤臣倍健还是喂不饱年轻人?

文|琥珀消研社 作者|朱古力 谁也想不到&#xff0c;进入三伏天以后首个运动潮流会是“晒背”。 小红书上近5万条“晒背”相关的笔记里&#xff0c;多数都是对“什么时候晒背”、“晒多久合适”等入门级问题的答疑解惑&#xff0c;微博热搜词条更是针对性地给出了“晒背最佳时…

小程序如何自定义分享内容

小程序项目中遇到门票转增功能&#xff0c;用户可将自己购买的门票分享给好友&#xff0c;好友成功领取即得门票一张 1.自定义分享按钮 通过button里的open-type属性里的share参数即自可定义分享按钮 <button open-type"share">分享</button>2.配置分…

使用 Torchvision 探测器进行实时深度排序

在实际应用中,跟踪是对象检测中最重要的组成部分之一。如果没有跟踪,实时监控和自动驾驶系统等应用就无法充分发挥其潜力。无论是人还是车辆,物体跟踪都起着重要作用。然而,测试大量的检测模型和重新识别模型是很麻烦的。为此,我们将使用 Deep SORT 和 Torchvision 检测器…

电路累积(放过压防反接、IIC、锂电池保护板)

一.防过压防反接电路 简单的过压保护电路一般加个TVS可以实现&#xff0c;当外部有瞬间高能量冲击时候它能够把这股能量抑制下来&#xff0c;虽然功率高&#xff0c;上千W都可以&#xff0c;但是维持抑制的时间很短很短&#xff0c;万一器件损坏或者长时间工作电压高于正常工作…

python入门篇03 基础案例 python版与java版 语法不同之处

目录 1. 前言: -> 上篇传送门: python入门篇02 python的语法基础 2. 案例: pzy超市的收银系统(控制台输入版) -> 2.0 需求摘要: -> 2.1 python代码答案: <直接可以运行> -> 2.2 java代码答案: <必须有main方法> 2.3 两种代码运行的结果: (一样…

424. 替换后的最长重复字符

424. 替换后的最长重复字符 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 424. 替换后的最长重复字符 https://leetcode.cn/problems/longest-repeating-character-replacement/description/ 完成情况&#xff…

集群安装OpenFOAM

在个人电脑上安装比较简单&#xff0c;在服务器上安装多出几个步骤&#xff0c;需要首先安装boost库以及openmpi库 下面步骤主要参考这篇博文&#xff0c;这里我自己重复一遍。 1、安装包准备 1.1、首先是下载OF源码和第三方库&#xff0c;这里采用wget的方式下载 wget -O …

【golang】怎样判断一个变量的类型?

怎样判断一个变量的类型&#xff1f; package mainimport "fmt"var container []string{"zero", "one", "two"} func main() {container : map[int]string{0: "zero", 1: "one", 2: "two"}fmt.Printf…

为什么边缘计算一定会迎来大爆发?

边缘计算是指在靠近数据源和终端设备的边缘位置进行数据处理、分析和存储的一种计算模式&#xff0c;相对于传统的云计算中心&#xff0c;它将计算资源尽可能地靠近数据源和终端设备。工业物联网是物联网在工业领域的应用&#xff0c;其中边缘计算在工业物联网中扮演着至关重要…

带你了解—通过远程访问局域网服务区并进行数据采集【内网穿透】

文章目录 前言1.使用cpolar进行局域网数据采集的案例2.远程数据隧道功能 前言 cpolar作为一款轻便易用、功能强大的内网穿透软件&#xff0c;在很多应用场景中都能大显身手。其中最常见的&#xff0c;就是网络网站方面的应用。利用cpolar建立的数据隧道&#xff0c;能够轻松实…

网易互娱出海之旅:大数据平台上云架构设计与实践

2020 年初&#xff0c;随着网易互娱的海外业务增长与海外数据合规的需求&#xff0c;我们开始了网易互娱大数据离线计算平台迁移出海的工作。前期&#xff0c;我们采取了云主机裸机加上高性能 EBS 块存储的方案。但是&#xff0c;这个方案存储费用高昂&#xff0c;成本是国内自…

【强化学习】值函数算法DQNs详解【Vanilla DQN Double DQN Dueling DQN】

DQNs【Vanilla DQN & Double DQN & Dueling DQN】 文章目录 DQNs【Vanilla DQN & Double DQN & Dueling DQN】1. DQN及其变种介绍1.1 Vanilla DQN1.2 Double DQN1.3 Dueling DQN 2. Gym环境介绍2.1 Obseravtion Space2.2 Reward Function2.3 Action Space 3. D…

Vue3 :Pinia入门

Vue3 &#xff1a;Pinia入门 Date: May 11, 2023 Sum: Pinia概念、实现counter、getters、异步action、storeToRefs保持响应式解构 什么是Pinia Pinia 是 Vue 的专属状态管理库&#xff0c;可以实现跨组件或页面共享状态&#xff0c;是 vuex 状态管理工具的替代品&#xff0c…

晨控CK-GW06-E01与汇川H5U系列PLC通讯手册

晨控CK-GW06-E01与汇川H5U系列PLC通讯手册 晨控CK-GW06-E01是一款支持标准工业通讯协议 EtherNet IP 的网关控制器,方便用户集成到PLC等控制系统中。本控制器提供了网络 POE 供电和直流电源供电两种方式&#xff0c;确保用户在使用无 POE 供电功能的交换机时可采用外接电源供电…