模拟string(四)详解

目录

  • 判断string大小关系
    • bool operator==(const string&s1,const string s2)
      • 代码
    • bool operator<(const string& s1, const string& s2)
      • 代码
    • bool operator<=(const string& s1, const string& s2)
      • 代码
    • bool operator>(const string& s1, const string& s2)
      • 代码
    • bool operator>=(const string& s1, const string& s2)
      • 代码
    • bool operator!=(const string& s1, const string& s2)
      • 代码
  • void clear()清除数据
    • 代码
  • ostream& operator<<(ostream& out, const string& s)实现string的流插入
    • 代码
  • istream& operator>>(istream& in, string& s)实现string的流提取
    • 代码
  • istream& getline(istream& in, string& s)获取输入的一行
    • 代码
  • string(const string& s)拷贝构造现代写法
    • 传统写法
    • 代码
    • 代码
  • 总结
  • 拓展1
  • 拓展2
    • 引用技术

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录

判断string大小关系

bool operator==(const string&s1,const string s2)

代码

bool operator==(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}

bool operator<(const string& s1, const string& s2)

代码

	bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;}

bool operator<=(const string& s1, const string& s2)

代码

bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}

bool operator>(const string& s1, const string& s2)

代码

bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}

bool operator>=(const string& s1, const string& s2)

代码

bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}

bool operator!=(const string& s1, const string& s2)

代码

bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}

void clear()清除数据

代码

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

ostream& operator<<(ostream& out, const string& s)实现string的流插入

代码

ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}

istream& operator>>(istream& in, string& s)实现string的流提取

代码

istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}

in.get()是遇到什么字符就直接取,因为有时候我们想要得到空格字符,但是用其他的输入会将空格视为结束
注意这里的string& s还没有开辟空间,所以ch = in.get()不能写成s[i]这样的形式

istream& getline(istream& in, string& s)获取输入的一行

代码

istream& getline(istream& in, string& s){s.clear();char ch;ch = in.get();char buff[128];size_t i = 0;while (ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}

string(const string& s)拷贝构造现代写法

拷贝构造的传统写法就是先开辟一块同样大小的空间,然后将数据拷贝到那块空间里

传统写法

        string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}

添加图片注释,不超过 140 字(可选)

在这里插入图片描述
在这里插入图片描述

代码

//现代写法
string(const string& s)
{string tmp(s._str);swap(tmp);
}

这个写法的过程如下图,首先this指针是表示的s2,s2会先指向空(不指向空会出现一些问题)

添加图片注释,不超过 140 字(可选)

然后进入函数后string tmp(s._str)会调用下面这个构造函数

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

所以tmp会拷贝s1的数据

添加图片注释,不超过 140 字(可选)

然后swap(tmp)就是让s2和tmp进行交换,因为this指针是s2
原因是string(const string& s)这个拷贝构造函数中的this指针就是s2,所以函数里面的this指针也默认是s2,交换后因为s2一开始就是指向的空,所以交换后tmp也指向空,而s2就指向tmp开出来的新空间

添加图片注释,不超过 140 字(可选)

因为tmp是一个局部对象,出了作用域后就会调用析构函数销毁
现在回到之前的问题为什么s2最开始是指向空的
因为如果s2不指向空,那么s2就是一个随机值,当s2和tmp交换后,tmp就指向一个随机值,tmp调用析构函数后会delete他的_str,但是tmp是一个随机值,delete一个随机值就会出现问题

string& operator=(const string& s)现代写法
现在需要将s3的值赋值给s1,注意赋值是两个已经存在的对象,所以他们的空间是已经开辟好了的
如果我们想直接将s3拷贝给s1的话需要分情况

添加图片注释,不超过 140 字(可选)

第一种情况就是s3的size大于s1的capacity,这样s1就需要扩容
第二种情况就是s3的size小于s1的capacity,但是如果s1的capacity远大于s1,比如capacity=1000,size=1
在实际当中一般不会这样拷贝数据,而是重新开一块新的空间,然后拷贝s3的数据,之后让s1指向新开的空间,然后释放s1之前的空间

添加图片注释,不超过 140 字(可选)

添加图片注释,不超过 140 字(可选)

添加图片注释,不超过 140 字(可选)

	string& operator=(const string& s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[]_str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}

这是上面的传统写法
现代写法过程如下,先创造一个对象s2拷贝s3的数据

添加图片注释,不超过 140 字(可选)

然后s1和s2交换,交换完之后因为s2是局部对象,出了作用域后会调用析构函数,所以s1之前的空间会被释放掉

添加图片注释,不超过 140 字(可选)

代码

//简化版
string& operator=(string& s2)
{swap(s2);return *this;
}

这个函数和第一个函数的区别是
第一个函数的传引用,所以s就是s3的别名,然后通过s3拷贝构造出s2,再将s2和s1进行交换
第二个函数是传值传参,传值传参会调用拷贝构造函数(形参是实参的拷贝)(这里要和之前的拷贝构造死循环问题要区分开,两个是不一样的),然后s2和s1进行交换

总结

现代写法相对于传统写法在二插树 链表等场景优势比较明显
因为树的节点如果用传统写法需要一个节点一个节点的开空间,然后再释放,而现代写法就是直接调用拷贝构造函数,然后就交换,并且省了自己去释放空间

拓展1

现在string这个类中的成员变量有下面这几个

添加图片注释,不超过 140 字(可选)

我们假设现在的环境是32位,现在需要计算他的sizeof()的大小
32位环境下指针的大小是4个字节,size_t的大小是4个字节,所以根据内存对齐规则,他的大小是12个字节,对于static修饰的变量不算进类里面,因为类里面的静态成员变量存的地方是在静态区里的,这个变量的生命周期是全局的,也就是我们可以把npos看成在类外面定义的全局变量

int main()
{std::string s1("11111");cout << sizeof(s1) << endl;jack::string s2("11111");cout << sizeof(s2) << endl;return 0;
}

添加图片注释,不超过 140 字(可选)

上面代码中我们用了两个不同的string,一个是库里的string,另一个是我们自己写的string,我们自己写的大小是12,而库里的string却是28
下面我们来讲解一下为什么库里的string是28
通过调试我们可以看到s1多了原始视图

添加图片注释,不超过 140 字(可选)

添加图片注释,不超过 140 字(可选)

这里的_Mysize和_Myres其实就是上面的size和capacity
那_Bx又是什么呢

_Bx点开后有三个部分_Buf ,_Ptr, _Alias(其实只有两个部分,编译器做了一下优化所以就有三个部分了)
我们再点开_Buf

添加图片注释,不超过 140 字(可选)

发现这个_Buf存的是字符,这些字符正好是我们输入
所以我们可以推断出这个_Buf可能就是多出来的那部分空间,因为_Buf是一个字符数组,里面有16个元素,他的大小就是16个字节,那么16+12正好就是28

也就是说我们定义的成员变量里面是有一个_Buf数组的,只是我们看不到而已

添加图片注释,不超过 140 字(可选)

那这个数组是固定大小的吗
其实当_size<16的时候数据就存在_Buf里面(_size不能等于16,因为需要留一个位置存放\0),我们来验证一下
这次的s1字符串里面有15个字符

添加图片注释,不超过 140 字(可选)

当变成16个字符的时候这个_Buf里面就没有字符了,因为装不下

在这里插入图片描述

那会存在哪呢?
_Ptr是一个指针,他会指向一块空间,而这块空间就是存放数据的地方
在这里插入图片描述

注意不同的编译器情况是不一样的,这是VS的环境下运行的,这种方法就是空间换时间,当_size<16的时候就不需要在堆上开空间了,而是在对象里面开空间,对象里面开的空间是在栈上的,因为栈上开空间要比堆上开更快,所以才这样搞

拓展2

在之前深拷贝的时候我们都需要先开一块同样大小的空间,然后拷贝数据

在这里插入图片描述

那我们能不能就让他们指向同一块空间,这样就省了开空间了

在这里插入图片描述

这样做有两个问题:第一个问题是会多次析构,第二个问题是其中一个修改会影响另一个

引用技术

为了解决这个问题我们需要用到一个引用技术
在了解到这个方法的时候我第一反应想到的是之前学过的一种做题方法,我们需要将arr1各个数字出现的次数统计出来

在这里插入图片描述

这个方法会需要有一个arr2的数组来记录,arr2的下标就对应arr1元素的值,arr2的下标对应的元素就是arr1元素出现的次数
就上面这个例子
0一共就在arr1里出现了1次,所以我们arr2[0]++(这里arr2中的所以元素初始化为0),
1一共出现了三次,所以arr2[1]++
2一共出现了1次,所以arr2[2]++

就这样遍历一遍arr1后就可以得到他每个数字出现的次数了

在这里插入图片描述

引用技术和这个比较类似
就是统计指向这块空间的对象有多少个,每增加一个对象就让计数的数字加1
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

当有对象要析构的时候就先让计数的数字减1

在这里插入图片描述
在这里插入图片描述

当计数的数字为1时表示这块空间只属于他自己,那么就可以放心的析构了

在这里插入图片描述

只不过这个方法只解决了第一个问题,第二个问题还是解决不了,所以当要修改的时候我们需要检查一下记录的数字,如果为1就直接修改,如果大于1就先拷贝再修改,然后数字减1

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

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

相关文章

算法板子:使用数组模拟队列——在队尾插入元素、在队头弹出元素、判断队列是否为空、查询队头元素

使用数组模拟时长这个样子&#xff1a; 代码&#xff1a; #include <iostream> using namespace std;const int N 1e5 10;// 数组q相当于队列; // hh是队头指针&#xff0c;始终指向队头 // tt是队尾指针&#xff0c;始终指向队尾 int q[N], hh, tt -1;// 队尾插入元…

代码随想录算法训练营第 25 天 | LeetCode491.递增子序列 LeetCode46.全排列 LeetCode47.全排列ii

代码随想录算法训练营 Day25代码随想录算法训练营第 25 天 | LeetCode491.递增子序列 LeetCode46.全排列 LeetCode47.全排列ii 目录 代码随想录算法训练营前言LeetCode491.递增子序列LeetCode46.全排列LeetCode47.全排列ii 一、LeetCode491.递增子序列1.题目链接2.思路3.题解 …

【算法】单向环形链表解决Josephu(约瑟夫)问题

应用场景 n 个小孩标号&#xff0c;逆时针站一圈。从 k 号开始&#xff0c;每一次从当前的小孩逆时针数 m 个&#xff0c;然后让最后这个小孩出列。不断循环上述过程&#xff0c;直到所有小孩出列&#xff0c;由此产生出一个队列编号。 提示 用一个不带头节点的循环链表来处…

FPGA开发——状态机的使用

一、概述 我们在使用FPGA进行开发的过程当中&#xff0c;实现一个东西用得最多的实现方法就是状态机的实现方法&#xff0c;用一句话总结就是万物皆可状态机&#xff0c;这和我们在学习Linux时常说的在Linux中万物都是文件差不多&#xff0c;这里就主要就是突出状态机的应用范…

技术实践—微前端技术应用

微前端是一种新兴的前端架构模式&#xff0c;是一种类似于微服务的架构&#xff0c;将微服务的理念应用于浏览器端。其核心理念是将一个大而单一的前端应用拆分为多个小型独立的微应用。这些微应用各自独立&#xff0c;可以由不同团队开发维护&#xff0c;部署&#xff0c;组合…

【调色板软件】免费、开源的调色板软件,焰火十二卷,提供了多种功能来生成一组调和色彩NO.108

本文一共:316 个字,需要阅读:1 分钟,更新时间:2024年7 月27日,部分内容具有时效性,如有失效请留言,阅读量:0 使用平台&#xff1a; Windows/macOS/CentOS/Ubuntu 由于我不是很懂&#xff0c;有需要的人自己摸索吧 资源来源于网络&#xff0c;免费分享仅供学习和测试使用&…

PostgreSQL 中如何重置序列值:将自增 ID 设定为特定值开始

我是从excel中将数据导入&#xff0c;然后再通过sql插入数据&#xff0c;就报错。 需要设置自增ID开始值 1、确定序列名称&#xff1a; 首先&#xff0c;需要找到与的增字段相关的序列名称。假设表名是 my_table 和自增字段是 id&#xff0c;可以使用以下查询来获取序列名称…

C 语言动态链表

线性结构->顺序存储->动态链表 一、理论部分 从起源中理解事物&#xff0c;就是从本质上理解事物。 -杜勒鲁奇 动态链表是通过结点&#xff08;Node&#xff09;的集合来非连续地存储数据&#xff0c;结点之间通过指针相互连接。 动态链表本身就是一种动态分配内存的…

【深度学习】LLaMA-Factory 大模型微调工具, 大模型GLM-4-9B Chat ,微调与部署 (2)

文章目录 数据准备chat评估模型导出模型部署总结 资料&#xff1a; https://github.com/hiyouga/LLaMA-Factory/blob/main/README_zh.md https://www.53ai.com/news/qianyanjishu/2015.html 代码拉取&#xff1a; git clone https://github.com/hiyouga/LLaMA-Factory.git cd …

万物互联,触手可及“2024南京智慧城市,物联网,大数据展会”

在金秋送爽的11月&#xff0c;南京这座历史悠久而又充满活力的城市&#xff0c;即将迎来一场科技盛宴——2024南京智慧城市、物联网、大数据展会。这不仅是一场技术的集会&#xff0c;更是未来生活蓝图的预览&#xff0c;它汇聚了全球顶尖的科技企业、创新者及行业精英&#xf…

【C++】循环结构-while语句

while 语句的语法格式&#xff1a; while (循环条件) {在满足循环条件下执行的操作} 注意要留有跳出循环的方式&#xff0c;避免死循环 1、不写 whlie (1)&#xff0c;写具体的循环条件 2、写while(1)&#xff0c;用 break 跳出循环 下面是一个实例 #include<iostream…

邮件攻击案例系列三:动态 IP 池爆破员工邮箱钓鱼重要客户

案例描述 2023 年 11 月&#xff0c;某制造业企业员工 Emily 接到海外客户电话&#xff0c;向其核实一封电子邮件的真实性&#xff0c;因为客户认为&#xff0c;该邮件所给出的链接不像是该公司的官网网址。Emily 查看自己的邮箱&#xff0c;并未发现客户所说的邮件。但从客户…

数据结构-----对列

前言 Hello, 小伙伴们&#xff0c;你们的作者菌又来了&#xff0c;前不久&#xff0c;我们学习了一种数据结构----栈&#xff0c;他特殊的性质使得他在一些数据管理的问题上被广泛的使用&#xff0c;那今天&#xff0c;我们就来学习另一种十分重要的数据结构--对列。 在开始之…

Linux字符设备驱动基本框架

本章我们从 Linux 驱动开发中最基础的字符设备驱动开始&#xff0c;重点学习 Linux 下字符设备驱动开发框架。本章会以一个虚拟的设备为例&#xff0c;讲解如何进行字符设备驱动开发&#xff0c;以及如何编写测试 APP 来测试驱动工作是否正常&#xff0c;为以后的学习打下坚实的…

3. 系统上电启动流程

1. 概述 上电启动&#xff0c;可参考恒玄sdk的指导手册。 注&#xff1a;可以在sdk这里加载自己的入口函数 STEP1&#xff1a; STEP2&#xff1a; STEP3&#xff1a; STEP4&#xff1a;

【日常记录】【插件】多媒体文本化: text-image 可以将文字、图片、视频进行「文本化」

文章目录 1. html基本结构2. 画文字3. 画图片4. 画视频参考地址 1. html基本结构 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-s…

ctfshow web入门 中期测评 web492--web502

web492 <?php include(render/render_class.php); include(render/db_class.php);$action$_GET[action]; if(!isset($action)){header(location:index.php?actionlogin);die(); }if($actioncheck){extract($_GET);if(preg_match(/^[A-Za-z0-9]$/, $username)){$sql &qu…

如何设置postgresql数据库的账户密码

说明&#xff1a;在我的云服务器上&#xff0c;postgres是使用yum的方式安装的&#xff0c;不需要设置postgres账户的密码&#xff0c;本文介绍安装后如何手动设置postgres账户的密码&#xff1b; postgres数据库安装&#xff0c;参考下面这篇文章&#xff1a; PostgreSQL安装…

SpringBoot整合SSE技术详解

Hi &#x1f44b;, Im shy SpringBoot整合SSE技术详解 1. 引言 在现代Web应用中,实时通信变得越来越重要。Server-Sent Events (SSE)是一种允许服务器向客户端推送数据的技术,为实现实时更新提供了一种简单而有效的方法。本文将详细介绍如何在SpringBoot中整合SSE,并探讨S…

python-学生排序(赛氪OJ)

[题目描述] 已有 a、b 两个链表&#xff0c;每个链表中的结点包括学号、成绩。要求把两个链表合并&#xff0c;按学号升序排列。输入格式&#xff1a; 输入共 NM1 行。 第一行&#xff0c;输入 a、b 两个链表元素的数量 N、M&#xff0c;中间用空格隔开。下来 N 行&#xff0c;…