使用柔性数组重写MyString

news/2024/11/22 22:53:47/

hello,各位宝子,今天阿崽将使用c++和柔性数组的方式重新去写String类

在开始本次知识前,首先给大家介绍下柔性数组这个buff特点:

结构中的柔性数组成员前面至少要包含一个其他成员 sizeof返回的这种结构大小不包括柔性数组的内存 包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以便于适应柔性数组预期的实际大小

感兴趣的宝子可以自己去看看柔性数组详细的内容。

目录

一.MyStirng 结构体设计

二.MyString类

2.1私有属性

2.2构造函数

2.3析构函数

2.4赋值函数

 2.5加法运算符重载(对象)

2.6+=运算符重载

2.7返回某下标元素

2.8改变某下标所对应的元素值


一.MyStirng 结构体设计

struct StrNode{int ref; // 标识该字符串被几个对象所持有int capa;//当前空间大小int len;//当前字符串大小char data[0]; //柔性数组,存储字符串,因为字符串长度未知所以使用柔型数组的方式};

二.MyString类

2.1私有属性

private:struct StrNode{int ref; int capa;int len;char data[0]; };StrNode* pstr;//用该指针去指向我们构造的结构体,因为指针只要四个字节大小(x86)省空间StrNode* GetNode(int total) {StrNode* s = (StrNode*)malloc(sizeof(StrNode) + sizeof(char) * total);if (nullptr == s) exit(EXIT_FAILURE);return s;}//为该结构体开辟空间MyString(StrNode* p) :pstr(p) {}//该函数在后面加法运算中详细介绍

2.2构造函数

MyString(const char* p = nullptr) :pstr(nullptr){if (p != nullptr){int len = strlen(p);//字符串长度,因为后面开辟的空间比字符串本身空间大所以不需要+1int total = len * 2;pstr = GetNode(total);pstr->ref = 1;pstr->len = len;pstr->capa = total - 1;strcpy_s(pstr->data,len+1, p);}}

 

结果我们通过调试的方式,监视该过程

 

2.3析构函数

~MyString(){if (pstr != nullptr && --pstr->ref == 0){free(pstr);}pstr = nullptr;}

在调用析构函数前有一个非常重要的点就是:当前字符串调用的对象个数,如果只有一个对象持有该字符串,可以直接调用析构函数,但是如果不为1,就说明有多个对象持有该资源,因为无法知道是否析构其他对象,所以我们只需要将ref这个指标减一,因为这些对象所指空间都是一样的,所以最后统一释放该字符串即可

 

2.4赋值函数

MyString& operator=(const MyString& s)  {if (this != &s) {if (pstr!=nullptr&&--pstr->ref==0) {delete[]pstr;}pstr = s.pstr;if (pstr != nullptr) {this->pstr->ref++;}}return *this;}

在使用赋值函数时(例:MyString s2;s2=s1;)首先要判断自身给自身赋值这个情况,其次要判断s2这个对象本身原来是否有指向字符串空间,并且指向该字符串的对象只有一个时释放该空间,将新的空间通过移动赋值方式直接赋值,然后指标加一。否则让s1中的ptr指向s2中的ptr。如果s2不为空那么指标加一,表示该s2有两个字符串

 

 2.5加法运算符重载(对象)

MyString operator+(const MyString& s) const{if (s.pstr == nullptr && this->pstr == nullptr) { return MyString(); }if (s.pstr == nullptr && this->pstr != nullptr) return *this;if (s.pstr != nullptr && this->pstr == nullptr) return s;int len = s.pstr->len + this->pstr->len;int total = len * 2;StrNode* n = (StrNode*)malloc(sizeof(StrNode) + sizeof(char) * total);if (nullptr == n) exit(EXIT_FAILURE);n->ref = 1;n->len = len;n->capa = total - 1;strcpy_s(n->data,this->pstr->len+1, this->pstr->data);strcat_s(n->data, len+2, s.pstr->data);return MyString(n);}

 加法是一个双目运算符,因为该方法是类中的方法,会有一个this指针所以形参这块只需要一个参数,其次我们要判断我们传进来的两个对象是否为空,对其他三种情况(s为空,this为空;s不为空,this为空;s为空,this不为空)依次进行判断。就到了两者都不为空,开辟一个新的空间依次保存这个两个对象所存储的字符串,注意最后的返回,因为n是结构体指针类型,而我们的构造函数没有这种构造方式,所以在私有属性中再添加一种构造方法:MyString(StrNode* p) :pstr(p) {}这样我们的加法才能运行。

会了这个函数,那么如果参数变为一个对象加一个字符串,一个字符串加一个对象都是同理,我们可以将字符串通过构造函数的方式然后加上对象就行(就是这个函数)

MyString operator+(const char* p, const MyString& s) {return MyString(p) + s;
}

2.6+=运算符重载

MyString& operator+=(const char* s) {if (this->pstr != nullptr && s != nullptr) {if (pstr->ref > 1) {int total = pstr->len + strlen(s);int le = pstr->len;pstr->ref -= 1;char* tmp = pstr->data;pstr = GetNode(total);pstr->ref = 1;pstr->len = total;pstr->capa = total * 2;strcpy_s(pstr->data, le, tmp);strcat_s(pstr->data, total + 1, s);}else {int total = pstr->len + strlen(s);if (pstr->capa< total){pstr = (StrNode*)realloc(pstr, sizeof(StrNode) + total * 2 + 1);pstr->capa = total * 2;}pstr->len = total;strcat_s(pstr->data,pstr->len+1,s);}}else if (this->pstr == NULL && s == NULL){pstr = NULL;pstr->ref += 1;}return *this;}

 这个函数比较复杂,我们一点点分析。

 首先我们需要判断传进来的两个对象的字符串是否都为空,如果都为空格,空加空还是空的,只要把空字符串的ref指标加一。如果都不为空,我们需要先判断加等对象的字符串是不是只有一个对象所持有,有可能是两个对象都指向“hello”字符串,而加等的对象只有一个,如果直接改变,另外一个也就会随之改变。所以我们使用ref这个指标-1,开辟一个新的空间,将原有的字符串拷贝进去,与他相等的对象不会产生混乱。如果只有一个就很简单,直接开辟新的空间,ref--,将相加的结果赋值给新空间,该对象直接指向新空间。

加等后

考虑到加等后将该对象赋值给另外一个对象,为了不重新调用赋值函数,我们使用引用的方式返回。就可以实现s2=s1+="hello"这个功能。

2.7返回某下标元素

char& operator[](const int index)const {if (index<0 || index>this->pstr->len) {exit(EXIT_FAILURE);}return pstr->data[index];}

2.8改变某下标所对应的元素值

	void revise(const int index, char p) {if (index<0 || index>this->pstr->len||this->pstr==nullptr) {exit(EXIT_FAILURE);}pstr->data[index] = p;}

这里我想的比较简单就是先判断下标元素是否正确后,然后直接改变,并没有考虑到原先持有该空间的对象个数,所以感兴趣的宝子们可以自行写下。

今天就更到这里啦,等我把这个MyString类的所有方式全部写完再与各位宝子分享。


http://www.ppmy.cn/news/75967.html

相关文章

leetCode刷题记录2

文章目录 hot100题560. 和为 K 的子数组581. 最短无序连续子数组 ▲617. 合并二叉树 hot100题 560. 和为 K 的子数组 560. 和为 K 的子数组 先暴力&#xff0c;过了再说 public int subarraySum(int[] nums, int k) {int ans 0;for (int i 0; i < nums.length; i) {in…

蓝库云|生产报工系统对制造业的作用,能给企业带来的质的飞跃

生产报工系统&#xff0c;对于做制造业的企业来说是再熟悉不过的软件系统了&#xff0c;不仅可以令制造企业可以快速响应客户需求&#xff0c;根据客户订购要求进行生产计划管理&#xff0c;还能生产报工可以帮助制造企业提升生产效率、提高产品质量、改善生产计划和提高客户满…

FreeRTOS(2)----任务管理

一&#xff0c;任务的基本概念 FreeRTOS是一个支持多任务的操作系统&#xff0c;多个任务可以共享一个优先级&#xff0c;当任务configUSE_TIME_SLICING 为 1&#xff0c;则可以使用时间调度的方式共享处理器。 简而言之&#xff0c;freertos任务就是一系列任务的集合。 二&…

Unity之OpenXR+XR Interaction Toolkit如何自定义VR按键

一.前言 上一篇文章我们介绍了Unity的新版本InputSyste如何使用,这一篇文章,我们主要说一下,在新版基于OpenXR的VR项目中,如何自定义VR按钮的功能。 二.Samples介绍 我们使用XR Interaction Toolkit插件时,它的几个Samples是非常有用的。如下所示: 它们分别是: 1.Sta…

SSH和SFTP是否相同

SSH和SFTP是否相同&#xff1f;SSH和SFTP是经典的对。在确保通信安全方面&#xff0c;它们交织在一起&#xff0c;尽管它们具有类似的功能&#xff0c;但它们并不是一回事。那么&#xff0c;它们之间有什么区别&#xff1f;请仔细阅读&#xff0c;找出答案。 什么是SSH&#x…

STM32 Simulink 自动代码生成电机控制——记录一次电机初始位置检测及NS极的判断实验

目录 前言 基本原理 仿真实现 代码生成及开发板验证 前言 之前做了脉振高频注入的仿真到代码生成开发板运行的实验&#xff0c;电机可以通过高频注入计算出角度&#xff0c;但是在初始位置检测的时候&#xff0c;尝试了不少方法但是效果一般&#xff0c;很容易反转&#xff…

[CTF/网络安全] 攻防世界 weak_auth 解题详析

[CTF/网络安全] 攻防世界 weak_auth 解题详析 弱认证弱认证绕过方法姿势Burp Suite 爆破 总结 题目描述&#xff1a;小宁写了一个登陆验证页面&#xff0c;随手就设了一个密码。 弱认证 weak_auth翻译&#xff1a;弱认证 这个术语通常用来描述一种较弱的安全认证方法或机制&am…

maven笔记

maven笔记 maven解决jar冲突的办法 排除A.jar中依赖的B.jar <dependency><groupId>xxx.xxx</groupId><artifactId>jarA</artifactId><version>0.0.1</version><exclusions><exclusion><groupId>xxx.xxx</grou…