使用柔性数组重写MyString

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);//字符串长度,因为后面开辟的空间比字符串本身空间大所以不需要+1
            int 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类的所有方式全部写完再与各位宝子分享。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构课程设计——哈夫曼编/译码器

数据结构课程设计任务书 学生姓名&#xff1a; 专业班级&#xff1a;软件工程 指导教师&#xff1a; 工作单位&#xff1a; 题 目: 哈夫曼编/译码器 基础要求&#xff1a; &#xff08;1&#xff09;熟悉各种…

数字信号处理基础(二):FFT和IFFT的使用以及详细分析代码书写思路

目录 1. fft和ifft的原理1.1 fft1.2 ifft 2. 书写代码思路3. 完整代码4. 结果图 1. fft和ifft的原理 1.1 fft fft是快速傅里叶变换&#xff0c;是MATLAB中计算信号频谱的函数&#xff0c;使用方法是fft(x)&#xff0c;直接对信号x进行fft计算。 由于fft函数计算信号的频谱是0…

vue3与vue2共存环境搭建

1、全局安装vue2 npm install vue-cli -g2、自行在任意位置创建一个文件夹&#xff0c;局部安装vue3 npm初始化 npm initnpm初始化 提示&#xff1a; 初始化后 出现文件package.json 如果没有初始化 会报错&#xff0c;且文件夹中不会新增内容 3、局部安装vue3 npm install …

宏工科技“全面”发力CIBF,助推电池智造“高效提质”

5月16-18日&#xff0c;第十五届中国国际电池技术展览会&#xff08;CIBF2023&#xff09;在深圳盛大举行。宏工科技携电池材料与电池匀浆领域的创新产品和系统解决方案精彩亮相。 据了解&#xff0c;宏工科技在新能源行业的业务涉及电池材料整线产线、电池匀浆、电池回收三个…

R语言实践——rWCVP入门

rWCVP入门 介绍1. 访问到WCVP1.1 方法一1.2 方法二&#xff08;谨慎&#xff09; 2. WCVP数据筛选2.1 关于按分类单元筛选的说明2.2 关于按分布区域筛选的说明 笔者实践 介绍 世界维管植物名录&#xff08;WCVP&#xff09;是维管植物物种的全球共识。它提供了科学已知的> …

【C语言】结构体指针

结构体指针 结构体基础知识注意对于成员的赋值 结构体指针指向结构体变量的指针结构体指针与结构体成员指针用结构体指针引用结构体成员 结构体 基础知识 初识结构体&#xff0c;可以先看这篇浅显易懂的文章结构体–基础篇 所谓结构体&#xff0c;是一组类型可以不同的相关变…

怎么把录音转文字?推荐你这三款工具

随着科技不断发展&#xff0c;录音转文字的技术也逐渐被广泛应用于各种场景中。其中最常见的一种就是会议记录。在日常工作中&#xff0c;会议是企业和组织中必不可少的一个环节&#xff0c;但在会议过程中的录音和记录往往需要花费大量的时间和精力。这个时候&#xff0c;我们…

基于MAC地址的ACL配置

基于MAC地址的ACL配置 【实验目的】 掌握基于MAC地址的标准ACL的配置。验证配置。 【实验拓扑】 实验拓扑如图1所示。 图1 实验拓扑 设备参数如表所示。 表1 设备参数表 设备 接口 IP地址 子网掩码 默认网关 S1 e0/0 N/A N/A N/A e0/1 N/A N/A N/A PC1 N/…

架构-软件工程模块-2

系统分析 数据流图可能出案例题&#xff0c;状态转换图了解作用即可 用例图、类图选择题多&#xff0c;暴徒了解即可 #mermaid-svg-lGozbtkYJPEQF1eo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lGozbtkYJPEQF1e…

c++学习——c与c++const修饰的变量的区别

c语言下const修饰的变量 1、c语言下const修饰的变量都有空间 2. c语言的const修饰的全局变量具有外部链接属性 07 const修饰的变量.c #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h>const int a 10;//常…

解析使用FPGA逻辑实现FIR滤波器的几种架构

有限脉冲响应(finite impulse response&#xff0c;FIR)数字滤波器 一、FIR数字滤波器理论介绍 FIR滤波器的实质就是输入序列与系统脉冲响应的卷积&#xff0c;即&#xff1a; 其中&#xff0c;N为滤波器的阶数&#xff0c;也即抽头数&#xff1b;x(n)为第n个输入序列&#xff…

AI人工智能标记数据的技术:类型、方法、质量控制、应用

AI人工智能 标记数据 在人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域中&#xff0c;标记数据是非常重要的一环。它是指对原始数据进行标记和注释&#xff0c;以便机器学习算法可以理解和利用这些数据。标记数据可以提高机器学习模型的准确…

研发项目工时统计工具哪个好?9大工时管理系统盘点

工时管理是项目型企业的重要需求&#xff0c;特别是在人力成本占比较高的行业&#xff0c;如软件开发、设计咨询、会计律师等。工时管理可以帮助企业核算项目人工成本&#xff0c;控制成本投入&#xff0c;提高项目利润&#xff0c;客观考核员工绩效&#xff0c;优化资源分配等…

HackTheBox-关卡Fawn

1. 连接靶场&#xff0c;打开FAWN实例场景&#xff0c;检查是否互通 TASK1 3 个字母的首字母缩写词 FTP 代表什么&#xff1f; 答案是&#xff1a;File Transfer Protocol TASK2 问题是&#xff1a;FTP服务通常监听哪个端口&#xff1f; FTP监听的TCP端口号为21,监听的数据端…

计算机操作系统(慕课版)第二章课后题答案

一、简答题 (1)什么是前趋图&#xff1f;试画出下面四条语句的前趋图. S1&#xff1a;axy&#xff1b; S2&#xff1a;bz1&#xff1b; S3&#xff1a;ca-b&#xff1b; S4&#xff1a;wc1&#xff1b; 答&#xff1a;前趋图(Precedence Graph)是一个有向无循环图&#xff0c;…

进程控制--进程的等待

回顾 之前我们已经学习了进程的状态和进程的退出如果你没有这些基础知识&#xff0c;应先去了解进程的相关基础知识。 这次我们主要来学习如何让进程等待子进程的退出。 为什么要等待子进程&#xff1f; 之前我们在学习进程的状态的时候&#xff0c;我们知道了进程有一种状态…

spring boot +Sa-Token优雅的实现项目鉴权!

1. 技术选型 最近在做登录、授权的功能&#xff0c;一开始考虑到的是spring boot spring security&#xff0c;但spring security太重&#xff0c;而我们是轻量级的项目&#xff0c;所以&#xff0c;spring security不适合我们。 而后考虑spring boot shiro&#xff0c;但s…

ChatGPT ✖️ 前端 = 有点er意思

HOT! HOT! HOT! &#x1f525; &#x1f525; &#x1f525; ChatGPT登上了国内各大平台的热搜榜&#xff0c;应该在去年11月末的时候就有不少同学了解并使用过&#xff0c;那个时候它刚刚问世&#xff0c;在互联网圈子里有了很大的热度&#xff0c;但是对于大众来说&#xff…

fastapi基础篇

文章目录 简介环境搭建安装基础文件自动文档 基础使用POST请求传递参数返回定制信息jinja2返回html 简介 FastAPI 是一个用于构建 API 的现代、快速&#xff08;高性能&#xff09;的 web 框架&#xff0c;使用 Python 3.6 并基于标准的 Python 类型提示。 关键特性 快速&#…

「 计算机网络 」TCP的粘包拆包问题

「 计算机网络 」TCP的粘包/拆包问题 参考&鸣谢 大病初愈&#xff0c;一分钟看懂TCP粘包拆包 雷小帅 TCP 的粘包拆包以及解决方案 一乐说 文章目录 「 计算机网络 」TCP的粘包/拆包问题一、前言二、为什么UDP没有粘包三、粘包拆包发生场景四、常见的解决方案五、Netty对粘包…