std::string的模拟实现

news2025/3/15 8:27:20

目录

string的构造函数

无参数的构造函数

根据字符串初始化

用n个ch字符初始化

用一个字符串的前n个初始化

拷贝构造

用另一个string对象的pos位置向后len的长度初始化

[ ]解引用重载

迭代器的实现

非const版本

const版本

扩容reserve和resize

reserve

resize

push_back和append

push_back

append

 +=运算符重载

insert插入

erase删除 

 find

substr

流插入和流提取

比较

赋值运算符重载

补充


string是库中用于储存字符串的类,其底层实际上是顺序表。

namespace cfl
{
	class string
	{
	public:

	private:
		char* _str;        //指向储存字符串的起始位置
		size_t _size;      //存储有效字符个数
		size_t _capacity;  //存储开辟的空间总个数
		static size_t npos;
    };
    	size_t string::npos = -1;
}

string中有一个静态变量是npos类型是size_t,但是定义是却定义为-1,所以npos强转为了无符号整形,也就是能存储size_t类型的最大值,其用来表示整个字符串。 


string的构造函数

参考文献:string::string - C++ Referencehttps://legacy.cplusplus.com/reference/string/string/string/

string的构造一共有中其中有一种的实现需要迭代器,在下面实现迭代器的时候讲解,先完成前6种。

无参数的构造函数

//无参数的构造函数
string()
	:_str(new char('\0'))   //此处不直接设置nullptr,将其设置为空字符串
	,_size(0)
	,_capacity(0)
{}

根据字符串初始化

//根据字符串初始化的构造函数
string(const char* str)
	:_size(strlen(str))
	, _capacity(strlen(str))
{
	_str = new char[_size];			//此处也可以在上面进行初始化,但是要注意顺序		
	memcpy(_str, str, _size + 1);   //_size+1是要拷贝'\0'
}

用n个ch字符初始化

//用n个ch字符初始化的构造函数
string(size_t n, const char ch)
	:_str(new char[n+1])  //要开辟n+1个空间是为了存储'\0'
	, _size(n)
	, _capacity(n)
{
	memset(_str,ch, n);
	_str[_size] = '\0';
}

用一个字符串的前n个初始化

//用一个字符串的前n个初始化的构造函数
string(const char* str, size_t n)
	:_str(new char[n + 1])
	, _size(n)
	, _capacity(n)
{
	memcpy(_str, str, n);
	_str[_size] = '\0';
}

拷贝构造

//拷贝构造函数
string(const string& s)
{
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	_size = s._size;
	memcpy(_str, s._str, _size);
	_str[_size] = '\0';
}

用另一个string对象的pos位置向后len的长度初始化

//用另一个string对象的pos位置向后len的长度初始化的构造函数
string(const string& s, size_t pos, size_t len = npos) //len给一个缺省值是npos,代表整个字符串
{
	if (len == npos || len + pos >= s._size)
	{
		len = s._size - pos;
		_capacity=len;
		_str = new char[_capacity + 1];
		_size = len;
		_str[_size] = '\0';
	}
	else
	{
		_capacity = len;
		_str = new char[_capacity + 1];
		_size = len;
		_str[_size] = '\0';
	}
}

[ ]解引用重载

string类对象可以像数组一样通过下标访问是因为解引用运算符重载。

char& operator[](size_t pos)
{
	return _str[pos];
}

迭代器的实现

迭代器实际上可以看成一个指针,但是其与指针有一些区别。范围for的底层逻辑就是迭代器,实现了迭代器,范围for才能使用。

非const版本

//迭代器的实现
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()  //begin就是返回起始位置的指针
{
	return _str;
}
iterator end()
{
	return _str + _size;  //返回最后一个字符下一个位置。
}

const版本

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

扩容reserve和resize

reserve

reserve扩容要帮助,传过去的参数比当前空间大才扩容,比当前空间小则不做处理。

void reserve(size_t n)
{
	if(n>_capacity)
	{
		//先开辟新的空间,再将数据拷贝回来
		char* tmp= new char[n + 1];
		memcpy(tmp, _str, _size + 1);
		_str = tmp;
		_capacity = n;
	}
}

resize

resize和reverse扩容有所区别,传给resize的空间小于_size会将n后面的所有数据删除,resize还会对像开辟的空间进行初始化。

void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		reserve(n);
		for (int i = _size; i < n; i++)
			_str[i] = ch;
		_str[_capacity] = '\0';
	}
}

push_back和append

push_back

尾插直接向顺序表结尾插入数据,尾插是也要考虑扩容。

void push_back(char ch)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = '\0';
}

append

appened是直接在尾部插入字符串。

void append(const char* s)
{
	size_t len = strlen(s);
	if (_capacity == _size || len + _size > _capacity)
	{
		reserve(len + _size);
	}
	memcpy(_str + _size, s, len + 1);
	_size = _size + len;
}

 +=运算符重载

string& operator+=(const char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* s)
{
	append(s);
	return *this;
}

insert插入

insert有两种:在pos位置插入n个字符ch,在pos插入字符串。

void insert(size_t pos, size_t n, char ch)
{
	assert(pos < _size);
	//判断空间够不够
	if (n + _size < _capacity)
	{
		reserve(n + _size);
	}
	//将数据向后移
	size_t end = _size;
	while (end >= pos)
	{
		_str[end + n] = _str[end];
		--end;
	}
	//插入数据
	for (int i = 0; i < n; i++)
		_str[pos + i] = ch;
}
void insert(size_t pos, const char* str)
{
	assert(pos < _size);
	size_t len = strlen(str);
	if (_size == _capacity || len + _size > _capacity)
	{
		reserve(len + _size);
	}
	int end = _size;
	while (end >= pos)
	{
		_str[end + len] = _str[end];
		--end;
	}
	for (int i = 0; i < len; i++)
		_str[pos + i] = str[i];
}

erase删除 

从pos位置删除len的长度。

void erase(size_t pos, size_t len = npos)
{
	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		int end = pos+len;
		while (end < _size)
		{
			_str[end - len] = _str[end];
			++end;
		}
		_size -= len;
		_str[_size] = '\0';
	}
}

 find

从pos位置向后找字符ch。

size_t find(size_t pos, const char ch)
{
	assert(pos < _size);
	int cur = pos;
	while (cur < _size)
	{
		if (_str[cur] == ch)
			return cur;
		
		++cur;
	}
	return npos;  //表示没找到
}

substr

返回冲pos位置开始取n个字符的子字符串。

string substr(size_t pos, size_t n)
{
	int end = pos + n;
	if (end > _size)
	{
		end = _size;
	}
	string s;
	for (int i = pos; i < end; i++)
		s += _str[i];
	return s;
}

流插入和流提取

流插入和流提取要放在类外定义,具体原因在<类和对象>该文种有讲解。

//流插入
ostream& operator<<(ostream& out, const cfl::string& s)
{
	for (auto e : s)
		out << e;  //使用循环for可以防止s中间有\0导致停止
	return out;
}
istream& operator>>(istream& in, cfl::string& s)
{
	//每次插入一个字符会导致每次尾插都要开空间
	//设置一个数组,先将字符插入到数组中,再将数组插入到s中
	char tmp[128];
	char ch = in.get();//此处使用in.get可以读取输入流中的空格
	int i = 0;
	while (ch != '\n')
	{
		tmp[i++] = ch;
		if (i == 127)
		{
			tmp[127] = '\0';
			i = 0;
			s += tmp;
		}
		ch = in.get();
	}
	tmp[i] = '\0';
	s += tmp;
	return in;
}

注意:流插入重载和流提取重载都是string的友元函数。


比较

此处仅实现>和==,其他比较可以直接套用他们两个。

bool operator<(const string& s)
{
	size_t end1 = _size;
	size_t end2 = s._size;

	int i = 0;
	while (i < end1 && i < end2)
	{
		if (_str[i] < s._str[i])
			return true;
		else if (_str[i] > s._str[i])
			return false;

		i++;
	}
	if (i < end1)
		return true;
	return false;
}

bool operator==(const string& s)
{
	if (_size != s._size)
		return false;
	int i = 0;
	while (i < _size)
	{
		if (_str[i] != s._str[i])
			return false;

		i++;
	}
	return true;
}

赋值运算符重载

void swap(string& s)
{
	std::swap(this->_str, s._str);
	std::swap(this->_capacity, s._capacity);
	std::swap(this->_size, s._size);
}


string& operator=(string tmp)
{
	swap(tmp);  //此处的tmp是临时变量,又是原对象的拷贝
				//所以直接将tmp与*this,交换,实现赋值
				//因为tmp是临时变量出作用域后会将*this的空间释放

	return *this;
}

补充

补充一些不是太常用的string的成员函数。

1)string::shrink_to_fit()由于将空间缩至有效数据处;

2)string::at(size_t n)返回n处的字符,相当于s1[n];

3)string::replace()替换类中的数据;

4)string::rfind()查找,但是是从后往前找;

5)size_t find_first_of(string& str,size_t pos=0),从pos位置向后找,找到str中的任意一个字符就返回;

6)size_t find_end_of(string& str,size_t pos=npos),已5)不同的是该函数从后往前找:

7)getline(cin,str),读取流中的数据,可指定读取到什么的时候停止,默认是换行符;

8)string to_string()可以将非字符串转化为字符串。

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

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

相关文章

探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)

文章目录 2.3.3 极化编码巴氏参数与信道可靠性比特混合生成矩阵编码举例 2.3.4 极化译码最小单元译码串行抵消译码&#xff08;SC译码&#xff09;算法SCL译码算法 2.3.5 总结**Polar 码的优势****Polar 码的主要问题****Polar 码的应用前景** 2.3.6 **参考文档** 本博客为系列…

汽车智能钥匙中PKE低频天线的作用

PKE&#xff08;Passive Keyless Entry&#xff09;即被动式无钥匙进入系统&#xff0c;汽车智能钥匙中PKE低频天线在现代汽车的智能功能和安全保障方面发挥着关键作用&#xff0c;以下是其具体作用&#xff1a; 信号交互与身份认证 低频信号接收&#xff1a;当车主靠近车辆时…

准备好了数据集之后,如何在ubuntu22.04上训练一个yolov8模型。

在Ubuntu 22.04上训练YOLOv8模型的步骤如下&#xff1a; 1. 安装依赖 首先&#xff0c;确保系统已安装Python和必要的库。 sudo apt update sudo apt install python3-pip python3-venv2. 创建虚拟环境 创建并激活虚拟环境&#xff1a; python3 -m venv yolov8_env source…

集合框架、Collection、list、ArrayList、Set、HashSet和LinkedHashSet、判断两个对象是否相等

DAY7.1 Java核心基础 集合框架 Java 中很重要的一个知识点&#xff0c;实际开发中使用的频录较高&#xff0c;Java 程序中必备的模块 集合就是长度可以改变&#xff0c;可以保存任意数据类型的动态数组 最上层是一组接口&#xff0c;接下来是接口的实现类&#xff0c;第三层…

JDK ZOOKEEPER KAFKA安装

JDK17下载安装 mkdir -p /usr/local/develop cd /usr/local/develop 将下载的包上传服务器指定路径 解压文件 tar -zxvf jdk-17.0.14_linux-x64_bin.tar.gz -C /usr/local/develop/ 修改文件夹名 mv /usr/local/develop/jdk-17.0.14 /usr/local/develop/java17 配置环境变量…

深度融合,智领未来丨zAIoT 全面集成 DeepSeek,助力企业迎接数据智能新时代

前言 Introduction 在数字化浪潮汹涌澎湃的当下&#xff0c;数据智能成为企业破局与创新的关键驱动力。zAIoT 作为云和恩墨面向 AIData 时代推出的数据智能平台软件&#xff0c;凭借其全面且强大的“采存算用”一体化功能体系&#xff0c;正在为航空航天、工业制造等领域和态势…

类和对象—多态—案例2—制作饮品

案例描述&#xff1a; 制作饮品的大致流程为&#xff1a;煮水-冲泡-倒入杯中-加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作产品基类&#xff0c;提供子类制作咖啡和茶叶 思路解析&#xff1a; 1. 定义抽象基类 - 创建 AbstractDrinking 抽象类&#xff0c;该类…

一周学会Flask3 Python Web开发-SQLAlchemy简介及安装

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射&#xff08;ORM&#xff09;工具&#xff0c;…

Golang学习笔记_41——观察者模式

Golang学习笔记_38——享元模式 Golang学习笔记_39——策略模式 Golang学习笔记_40——模版方法模式 文章目录 一、核心概念1. 定义2. 解决的问题3. 核心角色4. 类图 二、特点分析三、适用场景1. 股票价格监控系统2. 物联网设备状态监控3. 电商订单状态通知 四、Go语言实现示例…

中原银行:从“小机+传统数据库”升级为“OceanBase+通用服务器”,30 +系统成功上线|OceanBase DB大咖说(十五)

OceanBase《DB 大咖说》第 15 期&#xff0c;我们邀请到了中原银行金融科技部数据团队负责人&#xff0c;吕春雷。本文为本期大咖说的精选。 吕春雷是一位资历深厚的数据库专家&#xff0c;从传统制造企业、IT企业、甲骨文公司到中原银行&#xff0c;他在数据库技术与运维管理…

游戏引擎学习第140天

回顾并为今天的内容做准备 目前代码的进展到了声音混音的部分。昨天我详细解释了声音的处理方式&#xff0c;声音在技术上是一个非常特别的存在&#xff0c;但在游戏中进行声音混音的需求其实相对简单明了&#xff0c;所以今天的任务应该不会太具挑战性。 今天我们会编写一个…

LeetCode热题100JS(44/100)第八天|二叉树的直径|二叉树的层序遍历|将有序数组转换为二叉搜索树|验证二叉树搜索树|二叉搜索树中第K小的元素

543. 二叉树的直径 题目链接&#xff1a;543. 二叉树的直径 难度&#xff1a;简单 刷题状态&#xff1a;1刷 新知识&#xff1a; 解题过程 思考 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5] 输出&#xff1a;3 解释&#xff1a;3 &#xff0c;取路径 [4,2,1,3] 或…

力扣刷题DAY6(滑动窗口/中等+栈/简单、中等)

一、滑动窗口 找到字符串中所有字母异位词 方法一&#xff1a;哈希表 class Solution { public:vector<int> findAnagrams(string s, string p) {vector<int> ans;unordered_map<char, int> target;for (int i 0; i < p.size(); i) {target[p[i]];}in…

虚拟机 | Ubuntu图形化系统: open-vm-tools安装失败以及实现文件拖放

系列文章目录 虚拟机 | Ubuntu 安装流程以及界面太小问题解决 文章目录 系列文章目录虚拟机 | Ubuntu 安装流程以及界面太小问题解决 前言一、VMware Tools 和 open-vm-tools 是什么1、VMware Tools2、open-vm-tools 二、推荐使用open-vm-tools&#xff08;简单&#xff09;1、…

【DeepSeek】Ubuntu快速部署DeepSeek(Ollama方式)

文章目录 人人都该学习的DeepSeekDeepSeek不同版本功能差异DeepSeek与硬件直接的关系DeepSeek系统兼容性部署方式选择部署步骤&#xff08;Ollama方式&#xff09;1.选定适合的deepseek版本2.环境准备3.安装Ollama4.部署deepseek5.测试使用 人人都该学习的DeepSeek DeepSeek 作…

升级到Android Studio 2024.2.2 版本遇到的坑

一、上来就编译报错&#xff0c;大概率是因为选择了替换安装&#xff0c;本地配置文件出错 找到本地当前版本的配置文件&#xff0c;删掉&#xff0c;重启studio就好了&#xff1a; 1、打开终端 2、“cd /Users/用户名/Library/Application\ Support/Google” //到Google目录 …

【ESP-ADF】在 VSCode 安装 ESP-ADF 注意事项

1.检查网络 如果您在中国大陆安装&#xff0c;请使用魔法上网&#xff0c;避免无法 clone ESP-ADF 仓库。 2.VSCode 安装 ESP-ADF 在 VSCode 左侧活动栏选择 ESP-IDF:explorer&#xff0c;展开 advanced 并点击 Install ESP-ADF 然后会出现选择 ESP-ADF 安装目录。 如果出现…

我的两个医学数据分析技术思路

我的两个医学数据分析技术思路 从临床上获得的或者公共数据库数据这种属于观察性研究&#xff0c;是对临床诊疗过程中自然产生的数据进行分析而获得疾病发生发展的规律等研究成果。再细分&#xff0c;可以分为独立危险因素鉴定和预测模型构建两种。 独立危险因素鉴定是一直以…

FPGA-DE2115开发板实现4位全加器、3-8译码器。

文章目录 一、安装quartus二、4位全加器三、3-8译码器&#xff08;8段数码管&#xff09;四、参考文章 一、安装quartus 安装quartus参考文章&#xff1a;Quartus Prime 18.0与ModelSim的安装 Quartus II 18.0安装教程&#xff08;非常详细&#xff09;从零基础入门到精通&…

Pytorch xpu环境配置 Pytorch使用Intel集成显卡

1、硬件集显要为Intel ARC并安装正确驱动 2、安装Intel oneAPI Base Toolkit &#xff08;https://www.intel.cn/content/www/cn/zh/developer/tools/oneapi/base-toolkit-download.html&#xff09;安装后大约20G左右&#xff0c;注意安装路径 3、安装Visual Studio Build To…