String类 [中]

news2024/11/27 15:27:12

目录

一、 string 的深浅拷贝

0x00 构造函数与析构函数的实现

0x01 拷贝构造

 0x02 赋值

 0x03 整体代码

二、 string的实现

0x01 引入

 0x02 c_str

 0x03 默认构造函数

三、size()与operator[]的实现

0x01 size()的实现

0x02  operator[]的实现

0x03 遍历实现

 四、迭代器

0x01 迭代器的实现

0x02 再分析范围for


                                       

一、 string 的深浅拷贝

0x00 构造函数与析构函数的实现

#pragma once
#include<iostream>
using namespace std;

namespace yh
{
	class string
	{
	public:
        //进行构造函数,即进行初始化
		string(const char* str)//str不能改变,所以加const
			:_str(new char[strlen(str)+1])//为了能使_str之后能够修改,所以先分配空间
		{
			strcpy(_str,str);//再进行拷贝
		}
		//析构函数,即清理资源
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;//string应该是一个指针
	};
	void test_string1()
	{
		string s1("hello world");
	}
}

注意点: strlen:计算的是有效字符的个数,+1表示的是'\0'的空间

0x01 拷贝构造

问题引入:

默认的拷贝构造对于内置类型会进行浅拷贝,对于自定义类型会调用它的拷贝构造

假设是s2(s1),s1对s2的初始化; 如果成员变量是内置类型char* ,那么s2会和s1所指向的空间地址是相同的,但是在析构的时候,后创建的先析构,也就是先析构s2,而s1也是指向这块空间的,再析构不就会出现err了嘛

问题解决:

那么此时,就该到深拷贝出场了

	//拷贝构造
	string(const string& str1)
	    :_str(new char[strlen(str1._str)+1])
	{
		strcpy(_str,str1._str);
	}

从此图中可以看出,s1与s2所指向的空间是不同的,所以也就解决了上述的问题

 0x02 赋值

问题分析:

①如果s3空间比s1大或者相等,那没问题,但是如果比它小,还要考虑其他情况所以不如释放空间,重新创建和s1一样大的空间,再进行赋值

②如果自己给自己赋值,如s3 = s3,而开始s3这块空间已经被释放了,之后又去开一块这样的空间,然后又去访问被释放掉的空间中的值,这样难免会出错,所以判断一下,防止自己给自己赋值

//赋值重载 s3 = s1;
string& operator=(const string& str2)
{
	if (this != &str2)//防止自己赋值给自己
	{
		delete[] _str; //释放s3空间
		_str = new char[strlen(str2._str) + 1];//重新创建空间
		strcpy(_str, str2._str);//拷贝
	}
        return *this;
}

 但是上面的代码又会有一下小问题,虽然delete不会出现什么问题,但是new(开空间)是可能没有这么大的空间的,所以可能需要抛异常,所以我们可以对以上的代码进行一下调整,如下:

    if (this != &str2)//防止自己赋值给自己
	{
		char* temp = new char[strlen(str2._str) + 1];
		strcpy(temp, str2._str);
		delete[] _str;
		_str = temp;
	}

 0x03 整体代码

#pragma once
#include<iostream>
using namespace std;

namespace yh
{
	class string
	{
		//进行构造函数,即进行初始化
	public:
		string(const char* str)//str不能改变,所以加const
			:_str(new char[strlen(str)+1])//为了能使_str之后能够修改,所以先分配空间
		{
			strcpy(_str,str);//再进行拷贝
		}
		//析构函数,即清理资源
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		//拷贝构造
		string(const string& str1)
			:_str(new char[strlen(str1._str)+1])
		{
			strcpy(_str,str1._str);
		}
		//赋值重载 s3 = s1;
		string& operator=(const string& str2)
		{
			//if (this != &str2)//防止自己赋值给自己
			//{
			//	delete[] _str; //释放s3空间
			//	_str = new char[strlen(str2._str) + 1];//重新创建空间
			//	strcpy(_str, str2._str);//拷贝
			//}
			if (this != &str2)//防止自己赋值给自己
			{
				char* temp = new char[strlen(str2._str) + 1];
				strcpy(temp, str2._str);
				delete[] _str;
				_str = temp;
			}

			return *this;
		}
	private:
		char* _str;//string应该是一个指针
	};
	void test_string1()
	{
		string s1("hello world");
		string s2(s1);
		string s3("hello");
		s3 = s1;
	}
}

二、 string的实现

0x01 引入

以上是未考虑增删查改的情况,所以现在我们需要增加一下成员变量,进行增删查改的学习

char* _str;
int _size;//有效字符的个数
int _capacity;//有效字符的个数,不算'\0'

优化之后代码:

    class string
    {
		//进行构造函数,即进行初始化
	public:
		string(const char* str)
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str,str);//再进行拷贝
		}
		//析构函数,即清理资源
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		//拷贝构造
		string(const string& str1)
			:_size(str1._size)
			,_capacity(str1._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str,str1._str);
		}
		//赋值重载 s3 = s1;
		string& operator=(const string& str2)
		{
			if (this != &str2)//防止自己赋值给自己
			{
				char* temp = new char[str2._capacity + 1];
				strcpy(temp, str2._str);
				delete[] _str;
				_str = temp;
				_size = str2._size;
				_capacity = str2._capacity;//容量和大小记得更新
			}

			return *this;
		}
	private:
		char* _str;
		int _size;
		int _capacity;
	};

 0x02 c_str

c_str():返回当前字符串的首字符地址,会去寻找'\0',可读不可写

c_str的实现:

	const char* c_str() const 
	{
		return _str;
	}

 0x03 默认构造函数

问题引入:
①如果创建一个string s3;那么系统回去调用它的默认构造函数,所以我们还需要补充一个默认构造函数

② 因为c_str()是去找'\0'的,找不到'\0'可能会出现错误

不带参默认构造函数:

	string()
		:_str(new char[1])
		,_size(0)
		,_capacity(0)
	{
		_str[0] = '\0';
	}

全缺省构造函数:

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

此时,string s4;也可以去使用这个全缺省的构造函数,空字符串中默认有'\0',有效字符个数和容量还是为0,拷贝的时候将'\0'拷贝过去

 此时用全缺省和不带参的构造函数的效果是一样的

三、size()与operator[]的实现

0x01 size()的实现

size_t size()const//因为这个this指针不改变,所以加const
{
	return _size;
}

0x02  operator[]的实现

char& operator[](size_t pos)
{
	return _str[pos];//返回每个位置的字符
}

 同时,operator[]也可以充当写的功能:

s3[0] = 'w';
for (size_t i = 0;i < s3.size();i++)
{
	cout << s3[i] << " " ;
}
cout << endl;

问题引入:

此时普通对象是可以调用的,但是const对象呢?所以此时,我们在重载一个const版本,可读不可写

const char& operator[](size_t pos)const
{
	return _str[pos];//返回每个位置的字符
}

问题引入:

如何调用这个const版本的operator[]呢?

const char& operator[](size_t pos)const
{
	return _str[pos];
}
void f(const string& s3)
{
	cout << s3[0]	<< endl;
}
int main()
{
	string s3("hello");
	f(s3);
}

此时,s3传参可以当做是权限的缩小,变为了可读不可写,也就会调用带const版本的operator[]

0x03 遍历实现

size_t size()const
{
	return _size;
}
char& operator[](size_t pos)
{
    assert(pos < _size);//进行检验是否合理
	return _str[pos];
}
const char& operator[](size_t pos)const
{
    assert(pos < _size);
	return _str[pos];
}
int main()
{
	string s3("hello");
	for (size_t i = 0;i < s3.size();i++)
	{
		cout << s3[i] << " " ;
	}
	cout << endl;
}

 四、迭代器

迭代器:像指针一样的东西,但是可能并非指针,但是模拟了指针的部分功能

0x01 迭代器的实现

class string
{
	typedef char* iterator;
	iterator begin()
	{
		return _str;//首元素地址
	}
	iterator end()
	{
		return _str + _size;//最后一个元素的下一个位置
	}
}
int main()
{
	string s1("hello world");
	string::iterator it = s1.begin();//内嵌类型,表示string的迭代器
	string::iterator temp = it;
	while (it != s1.end())//迭代器写
	{
		*it += 1;
		it++;
	}
	it = temp;
	while (it != s1.end())//迭代器读
	{
		cout << *it << " ";
		it++;
	}
}

其次,就是const迭代器,只能读,不能写

typedef const char* iterator;
const iterator begin()const
{
	return _str;//首元素地址
}
const iterator end()const
{
	return _str + _size;//最后一个元素的下一个位置
}

0x02 再分析范围for

for (auto e:s1)
{
	cout << e << " ";
}

范围for在编译的时候被替换为了迭代器,只要把迭代器实现好,那么范围for自然而然就可以运用了

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

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

相关文章

洛谷B2098 整数去重

整数去重 题目描述 给定含有 n n n 个整数的序列&#xff0c;要求对这个序列进行去重操作。所谓去重&#xff0c;是指对这个序列中每个重复出现的数&#xff0c;只保留该数第一次出现的位置&#xff0c;删除其余位置。 输入格式 输入包含两行&#xff1a; 第一行包含一个…

获取两个日期间时长 (XX天XX时XX分)

使用场景&#xff1a; 发货日期与到货日期 计算运输时长 代码&#xff1a; private String getMinuteTime(String startTime, String endTime) {String minuteTime null;if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) {long minute DateUti…

【芯片设计- RTL 数字逻辑设计入门 2 - vcs 及 verdi 使用介绍】

文章目录 1.1 VCS 编译环境1.1.1 Complie Design1.1.2 simv 仿真 1.2 VCS 波形生成及查看1.2.1 verdi 命令介绍1.2.2 verdi 波形查看 1.1 VCS 编译环境 VCS 全称是 Verilog Compiler Simulator&#xff0c;是 Synopsys 公司的&#xff0c;类似于windows环境下的 questasim 或 …

Flink sql

1.创建表的执行环境 第一种 StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); SingleOutputStreamOperator<Event> streamOperator env.addSource(new ClickSource()).assignTimestampsAndWatermarks(W…

深入理解C语言指针

目录 一、指针基础知识 二、野指针 三、指针运算 四、二级指针 五、指针数组与数组指针 六、函数指针与函数指针数组 一、指针基础知识 什么是指针&#xff1f; 指针其实就是个用来存放地址的变量&#xff0c;定义为type *。 指针大小&#xff1f; 32位平台(32个地…

【大数据-调度工具】dolphinscheduler安装和遇到的问题

1.安装 安装步骤按照官网安装即可 官网&#xff1a;DolphinScheduler | 文档中心 (apache.org) 版本&#xff1a;3.1.5 2.踩坑记录 Q1.大文件无法上传 问题描述&#xff1a; 在资源中心中上传文件选择完大文件夹之后&#xff0c;选择确认之后确认按钮转了几圈圈之后就没…

[Element]调整select样式

通过伪元素&#xff0c;实现这个和step长得差不多的样式 <template><el-selectv-model"value"placeholder"请选择提报单位"style"width: 430px"><el-optionv-for"(item, i) in officeList":class"el-option get…

hive的基本操作语句

背景&#xff1a;记录一下hive创建数据库&#xff0c;建表&#xff0c;添加数据&#xff0c;创建分区等的语句吧&#xff0c;省得总百度&#xff0c;&#x1f604; 第一步&#xff1a;hive的建库语句 create database pdata_dynamic;查看是否创建成功了 show databases;显示如…

Jenkins入门教程

一、开始使用 Jenkins 本导读将向您介绍使用 Jenkins、Jenkins 的主要特性和 Jenkins Pipeline 的基本知识。 本导读使用“独立”的 Jenkins 发行版&#xff0c;它可以在您自己本地的机器上运行。 准备工作 第一次使用 Jenkins&#xff0c;您需要&#xff1a; 机器要求&…

从入门到实践:计算机视觉图像分类完全指南

计算机视觉图像分类是计算机视觉领域的一个重要分支&#xff0c;它的目标是让计算机能够识别并分类不同的图像。在本文中&#xff0c;我们将介绍计算机视觉图像分类的基本概念、流程和常用算法。 一、图像分类的基本概念 图像分类是指将输入的图像自动分类到预定义的一组类别中…

量子计算+真实材料模拟!美国埃姆斯国家实验室科学家获得新突破

​ 噪声量子比特ADAPT仿真&#xff08;图片来源&#xff1a;网络&#xff09; 量子计算机的潜在功能远远超出当今的经典计算机&#xff0c;来自美国能源部埃姆斯国家实验室的科学家们展示了一种在材料研究中利用量子计算的新方法&#xff0c;通过模拟材料的自适应算法&#xf…

自参考和对比学习正则化的Few-shot医学图像分割

文章目录 Few-shot Medical Image Segmentation Regularized with Self-reference and Contrastive Learning摘要本文方法Local Prototype-Based SegmentationSelf-reference RegularizationContrastive LearningSuperpixel-Based Self-supervised Learning 实验结果 Few-shot …

分布式搜索引擎2——深入elasticsearch

数据聚合 聚合的分类 聚合(aggregations)可以实现对文档数据的统计、分析、运算。聚合常见的有三类: 桶(Bucket)聚合:用来对文档做分组 TermAggregation:按照文档字段值分组Date Histogram:按照日期阶梯分组&#xff0c;例如一周为一组&#xff0c;或者一月为一组 度量&…

第10章:数据处理增删改

一、插入数据 CREATE TABLE emp1 (id int(11) ,name varchar(15) ,hire_date date ,salary double(10,2) )1.添加一条数据 ①没有指明添加的字段&#xff0c;一定按照顺序添加 insert into emp1 values(1,wang,2000-4-4,5900)②指明添加的字段&#xff08;推荐&#xff09;…

【 图像水印 2019 CVPR】 StegaStamp 论文翻译

【 图像水印 2019 CVPR】 StegaStamp 论文翻译 论文题目&#xff1a;StegaStamp: Invisible Hyperlinks in Physical Photographs 中文题目&#xff1a;物理照片中不可见的超链接 论文链接&#xff1a;https://arxiv.org/abs/1904.05343 论文代码&#xff1a;https://github.co…

QxRibbon 知:openEuler 23.03 搭建 Qt5 开发环境

文章目录 安装 openEuler 23.03 虚拟机安装 GNOME 桌面环境安装 Qt5 开发环境构建 QxRibbon参考资料 安装 openEuler 23.03 虚拟机 VMware 安装 openEuler 23.03 虚拟机 平台&#xff1a;x86_64 虚拟机配置&#xff1a;4核、4G内存、100G磁盘 ISO 镜像&#xff1a;https://mir…

FT2000+ qemu kvm 红旗 crash 分析 频繁设置CPU online导致进程卡死、不调度故障

测试程序 /** tcti.cpp参考&#xff1a; https://www.cnblogs.com/organic/p/17321523.htmlg -stdc11 -lpthread trigger_cgroup_timer_inactive.cpp -o inactive_timer ./inactive_timer 100000 10000 */#include <errno.h> #include <iostream> #include <pt…

Redis进阶(集群,雪崩,击穿,穿透.......)

Redis进阶 Redis事务_事务的概念与ACID特性 Redis的事物不保证原子性 数据库层面事务 在数据库层面&#xff0c;事务是指一组操作&#xff0c;这些操作要么全都被成功执行&#xff0c;要么全都不执行。 数据库事务的四大特性 A&#xff1a;Atomic&#xff0c;原子性&#xf…

Docker笔记5 | 容器的基本操作

5 | 容器的基本操作 1 启动容器1.1 启动方式1.2 新建容器并启动1.3 docker run时的运行过程1.4 启动已终止容器1.5 后台运行1.6 查看容器信息 2 终止容器3 进入容器3.1 docker attach3.2 docker exec 4 导入导出容器4.1 导出容器4.2 导入容器 5 删除容器 1 启动容器 1.1 启动方…

Linux内核主要组成部分有哪些?

Linux 内核由几大子系统构成&#xff0c;分别为进程调度、进程间通信&#xff08;IPC&#xff09; 、内存管理、虚拟 文件系统和网络接口。这几大子系统既相互独立又有非常紧密的关联。图 3-5 展示了内核的 几大子系统之间以及这些子系统和计算机系统的其他模块之间的关系。 接…