C++初阶 -- 手撕string类(模拟实现string类)

news2025/2/2 13:55:40

目录

一、string类的成员变量

二、构造函数

2.1 无参版本

2.2 有参版本

2.3 缺省值版本

三、析构函数

四、拷贝构造函数

五、c_str函数

六、operator=重载

七、size函数

八、迭代器iterator

8.1 正常版本

8.2 const版本

九、operator[]

9.1 正常版本

9.2 const版本

十、reserve函数

十一、push_back函数

十二、append函数 -- 字符串版本

十三、operator+=

13.1 尾插字符

13.2 尾插字符串

十四、insert函数

十五、erase函数

十六、find函数

十七、swap函数

十八、 substr函数

十九、clear函数

二十、operator<< 和 operator >>


一、string类的成员变量

private:
	char* _str;
	size_t _size;
	size_t _capacity;

	const static size_t npos = -1;

二、构造函数

2.1 无参版本

// 无参
string()
{
	_str = new char[1];
	_str[0] = '\0';
	_size = 0;
	_capacity = 0;
}

2.2 有参版本

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

2.3 缺省值版本

事实上,string类的构造函数不可能有两个版本。所以可以使用缺省值来实现构造函数

// 缺省参数
string(const char* str = "")
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

唯一需要注意的是缺省参数该怎么给?

  • 由于strlen()是遍历字符串直到遇到'\0'才结束,所以缺省值不能给nullptr,因为strlen会对指针进行解引用。
  • 也不能给'\0'。因为'\0'是字符常量,而str是字符指针,它两类型不匹配,所以也会报错。

三、析构函数

当_str指向的空间为空时,delete函数内部会有判断机制。如果是空指针,delete函数不会运行。

// 析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

四、拷贝构造函数

// 拷贝构造函数
string(const string& str)
{
	// 传统版本
	_str = new char[str._capacity + 1];
	strcpy(_str, str._str);

	_size = str._size;
	_capacity = str._capacity;
}

五、c_str函数

// c_str()函数
const char* c_str() const
{
	return _str;
}

六、operator=重载

// operator= 重载
string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;

		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}

	return *this;
}

七、size函数

// size()函数
size_t size() const
{
	return _size;
}

八、迭代器iterator

8.1 正常版本

typedef char* iterator;

// 非const版本的迭代器
iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

8.2 const版本

typedef const char* const_iterator;

// const版本的迭代器
const_iterator begin() const
{
	return _str;
}

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

九、operator[]

  • operator[]运算符重载,由于string类对象并不是内置类型,所以想要获取字符串里的字符就得用[下标]的方式。
  • operator[]不仅仅可以读也可以写,所以返回类型就不能是char,而是使用引用返回char&。

9.1 正常版本

// []运算符重载 -- 非const版本
char& operator[](size_t pos)
{
	assert(pos <= _size);

	return _str[pos];
}

 

9.2 const版本

// []运算符重载 -- const版本
const char& operator[](size_t pos) const
{
	assert(pos <= _size);

	return _str[pos];
}

十、reserve函数

由于_size和_capacity只包括有效字符而不包括'\0'。所以要默认多开一个空间来存放'\0'。

// reserve()函数 -- 新开一个大小为n+1的空间,然后把原数组的数据拷贝到新数组,再delete原数组,最后指针指向新数组
void reserve(size_t n)
{
	if (n > _capacity)
	{
		// 开辟的空间+1代表给'\0'也开了个空间
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

 

十一、push_back函数

  • 对于追加单个字符来说,因为追加字符的数量是已知的,所以可以用reserve函数来控制字符串的容量。
  • 在追加字符之前首先得先判定当前字符的数量是否等于字符串的容量。等于就扩容。不等于就正常插入即可。
// push_back()函数
void push_back(char ch)
{
	if (_size == _capacity)
	{
		size_t NewCapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(NewCapacity);
	}

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

十二、append函数 -- 字符串版本

字符串相较于字符比较难以控制追加的数量,所以得先求出追加字符串的长度,再判断追加字符串的长度 + 当前字符串的长度是否大于当前容器的容量。

大于就扩容,不大于就使用strcpy函数在已重新扩容的字符串中尾插待插入字符串。

// append()函数
void append(const char* s)
{
	size_t len = strlen(s);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}

	strcpy(_str + _size, s);
	_size += len;
}

十三、operator+=

13.1 尾插字符

尾插字符就复用push_back函数

// operator+=重载 -- 字符
string& operator+= (char ch)
{
	push_back(ch);

	return *this;
}

13.2 尾插字符串

尾插字符串就复用append函数

// operator+=重载 -- 字符串
string& operator+= (const char* str)
{
	append(str);

	return *this;
}

 

十四、insert函数

// insert()函数 -- 插入字符串
string& insert(size_t pos, const char* str)
{
	assert(pos < _size);

	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}

	//size_t end = _size;
	int end = _size;
	while (end >= (int)pos) // 防止发生隐式类型转换
	{
		_str[end + len] = _str[end];
		--end;
	}

	strncpy(_str + pos, str, len);
	_size += len;

	return *this;
}

十五、erase函数

首先得先判断len是否等于缺省值npos或者 len + pos 是否大于等于字符串实际存储有效字符的数量。如果出现以上两种情况直接在字符串中下标为pos的值赋值为'\0',再把pos赋给_size。

// erase()函数
void erase(size_t pos = 0, size_t len = npos)
{
	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
	}
}

十六、find函数

用strstr函数找到要找字符串的地址,如果strstr函数返回值为空指针就返回npos。

反之则用返回的指针 - 当前字符串的指针。

// find()函数 -- 寻找字符
size_t find(char ch, size_t pos = 0) const
{
	for (size_t i = pos; i < _size; i++)
	{
		if (ch == _str[i])
		{
			return i;
		}
	}
	return npos;
}

// find()函数 -- 寻找字符串
size_t find(const char* s , size_t pos = 0) const
{
	const char* ptr = strstr(_str + pos, s);

	if (ptr == nullptr)
	{
		return npos;
	}
	else
	{
		return ptr - _str;
	}
}

十七、swap函数

// swap()函数
void swap(string& str)
{
	std::swap(_str, str._str);
	std::swap(_size, str._size);
	std::swap(_capacity, str._capacity);
}

十八、 substr函数

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

	size_t end = pos + len;
	if (len == pos || pos + len >= _size)
	{
		end = _size;
	}

	string str;

	// 开一个新空间给子字符串,容量为end - pos
	str.reserve(end - pos);

	// 尾插到新字符串中
	for (size_t i = pos; i < len; i++)
	{
		str += _str[i];
	}
	return str;
}

十九、clear函数

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

二十、operator<< 和 operator >>

// 流插入重载 <<
ostream& operator<< (ostream& out, const string& s)
{
	for (char ch : s)
	{
		out << ch;
	}

	return out;
}

// 流提取重载 >>
istream& operator>> (istream& in, string& s)
{
	s.clear();

	// 提取缓冲区里的字符,遇到' '和'\n'就结束
	char ch = in.get();
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	buff[i] = '\0';
	s += buff;

	return in;
}

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

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

相关文章

【Unity3D】实现2D角色/怪物死亡消散粒子效果

核心&#xff1a;这是一个Unity粒子系统自带的一种功能&#xff0c;可将粒子生成控制在一个Texture图片网格范围内&#xff0c;并且粒子颜色会自动采样图片的像素点颜色&#xff0c;之后则是粒子编辑出消散效果。 Particle System1物体&#xff08;爆发式随机速度扩散10000个粒…

85.[1] 攻防世界 WEB easyphp

进入靶场 属于代码审计 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;常用于调试或展示代码 highlight_file(__FILE__);// 初始化两个标志变量&#xff0c;用于后续条件判断 $key1 0; $key2 0;// 从 GET 请求中获取参数 a 和 b $a $_GET[a]; $b $_GET[b];// 检…

pytorch图神经网络处理图结构数据

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 图神经网络&#xff08;Graph Neural Networks&#xff0c;GNNs&#xff09;是一类能够处理图结构数据的深度学习模型。图结构数据由节点&#xff08;vertices&#xff09;和边&#xff08;edges&#xff09;组成&a…

海外问卷调查,最常用到的渠道查有什么特殊之处

市场调研&#xff0c;包含市场调查和市场研究两个步骤&#xff0c;是企业和机构根据经营方向而做出的决策问题&#xff0c;最终通过海外问卷调查中的渠道查&#xff0c;来系统地设计、收集、记录、整理、分析、研究市场反馈的工作流程。 市场调研的工作流程包括&#xff1a;确…

【Uniapp-Vue3】解决uni-popup弹窗在安全区显示透明问题

我们在使用uni-popup时&#xff0c;如果想要给弹出内容添加一个背景颜色&#xff0c;我们会发现在安全区域是不显示该背景颜色的。 首先根据如下的目录结构找到uni-popup.vue文件 在该文件中找到bottom配置&#xff0c;将红箭头所指代码注释掉 下面的安全区域就没有了&#xff…

项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser

文章目录 一、情景说明二、解决办法 一、情景说明 在重写若依后端服务的过程中 使用了Redis存放LoginUser对象数据 那么&#xff0c;有存就有取 在取值的时候&#xff0c;报错 二、解决办法 方法1、在TokenService中修改如下 getLoginUser 方法中&#xff1a;LoginUser u…

核心集:DeepCore: A Comprehensive Library for CoresetSelection in Deep Learning

目录 一、TL&#xff1b;DR 二、为什么研究核心集&#xff1f; 三、问题定义和如何做 3.1 问题定义 3.2 业界方法 3.2.1 基于几何的方法 3.2.2 基于不确定性的方法 3.2.3 基于误差/损失的方法 3.2.5 GraNd 和 EL2N 分数 3.2.6 重要性采样 3.2.7 基于决策边界的办法 …

Hot100之矩阵

73矩阵置零 题目 思路解析 收集0位置所在的行和列 然后该行全部初始化为0 该列全部初始化为0 代码 class Solution {public void setZeroes(int[][] matrix) {int m matrix.length;int n matrix[0].length;List<Integer> list1 new ArrayList<>();List<…

可视化相机pose colmap形式的相机内参外参

目录 内参外参转换 可视化相机pose colmap形式的相机内参外参 内参外参转换 def visualize_cameras(cameras, images):fig plt.figure()ax fig.add_subplot(111, projection3d)for image_id, image_data in images.items():qvec image_data[qvec]tvec image_data[tvec]#…

数据库内存与Buffer Pool

数据库内存与Buffer Pool 文章目录 数据库内存与Buffer Pool一&#xff1a;MySQL内存结构1&#xff1a;MySQL工作组件2&#xff1a;工作线程的本地内存3&#xff1a;共享内存区域4&#xff1a;存储引擎缓冲区 二&#xff1a;InnoDB的核心&#xff1a;Buffer Pool1&#xff1a;数…

程序员学英文之At the Airport Customs

Dialogue-1 Making Airline Reservation预定机票 My cousin works for Xiamen Airlines. 我表哥在厦航上班。I’d like to book an air ticket. 我想预定一张机票。Don’t judge a book by its cover. 不要以貌取人。I’d like to book / re-serve a table for 10. 我想预定一…

Redis代金卷(优惠卷)秒杀案例-单应用版

优惠卷表:优惠卷基本信息,优惠金额,使用规则 包含普通优惠卷和特价优惠卷(秒杀卷) 优惠卷的库存表:优惠卷的库存,开始抢购时间,结束抢购时间.只有特价优惠卷(秒杀卷)才需要填写这些信息 优惠卷订单表 卷的表里已经有一条普通优惠卷记录 下面首先新增一条秒杀优惠卷记录 { &quo…

51单片机 01 LED

一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC&#xff1b;如果没有找到hex文件&#xff08;在objects文件夹下&#xff09;&#xff0c;在keil中options for target-output- 勾选 create hex file。 如果要修改编程 &#xff1a;重新编译-下载/编程-单片机重…

MusicFree-开源的第三方音乐在线播放和下载工具, 支持歌单导入[对标落雪音乐]

MusicFree 链接&#xff1a;https://pan.xunlei.com/s/VOI0RrVLTTWE9kkpt0U7ofGBA1?pwd4ei6#

消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)

1、RabbitMQ 特点&#xff1a; AMQP协议&#xff1a;RabbitMQ是基于AMQP&#xff08;高级消息队列协议&#xff09;构建的&#xff0c;支持多种消息传递模式&#xff0c;如发布/订阅、路由、RPC等。多语言支持&#xff1a;支持多种编程语言的客户端库&#xff0c;包括Java、P…

nacos 配置管理、 配置热更新、 动态路由

文章目录 配置管理引入jar包添加 bootstrap.yaml 文件配置在application.yaml 中添加自定义信息nacos 配置信息 配置热更新采用第一种配置根据服务名确定配置文件根据后缀确定配置文件 动态路由DynamicRouteLoaderNacosConfigManagerRouteDefinitionWriter 路由配置 配置管理 …

(笔记+作业)书生大模型实战营春节卷王班---L0G2000 Python 基础知识

学员闯关手册&#xff1a;https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 课程视频&#xff1a;https://www.bilibili.com/video/BV13U1VYmEUr/ 课程文档&#xff1a;https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 关卡作业&#xff1a;htt…

SpringBoot中Excel表的导入、导出功能的实现

文章目录 一、easyExcel简介二、Excel表的导出2.1 添加 Maven 依赖2.2 创建导出数据的实体类4. 编写导出接口5. 前端代码6. 实现效果 三、excel表的导出1. Excel表导入的整体流程1.1 配置文件存储路径 2. 前端实现2.1 文件上传组件 2.2 文件上传逻辑3. 后端实现3.1 文件上传接口…

动态规划DP 背包问题 完全背包问题(题目分析+C++完整代码)

概览检索 动态规划DP 概览&#xff08;点击链接跳转&#xff09; 动态规划DP 背包问题 概览&#xff08;点击链接跳转&#xff09; 完全背包问题 原题链接 AcWiing 3. 完全背包问题 题目描述 有 N种物品和一个容量是 V的背包&#xff0c;每种物品都有无限件可用。 第 i种物…

【cocos creator】【模拟经营】餐厅经营demo

下载&#xff1a;【cocos creator】模拟经营餐厅经营