C++ 之 string类的模拟实现

这学习我有三不学

昨天不学,因为昨天是个过去

明天不学,因为明天还是个未知数

今天不学,因为我们要活在当下,我就是玩嘿嘿~

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

目录

一、string类的模拟实现

1.成员函数(Member functions)

1.1 构造函数(constructor)

1.2 析构函数(destructor)

1.3 赋值拷贝函数(operator=)

2.迭代器(iterators)

3.容量(Capacity)

4.元素访问(Element access)

5.调节器(Modifiers)

6.字符操作(String operation)

7.成员常量(Member constant)npos实现

8.非成员函数重载(Non-member function overload)

二、完结撒❀


前言:

模拟string类的实现对于我们学习认识string类会有更加深刻的理解,还没学过string类的老铁建议可以先看学习一下我的上一篇博客讲解:C++ 之 string类 详细讲解,再来进行模拟实现。

一、string类的模拟实现

在上篇博客中讲解了string类的常用接口,这篇博客带大家模拟实现一下string类的一些常用接口。

string类查阅文档

我们根据上面文档所规划的接口分类为大家进行部分模拟实现,大家可以先简单看一下上面文档。

1.成员函数(Member functions)

1.1 构造函数(constructor)

● 无参构造函数 string() 实现:

string()//:_str(nullptr):_str(new char[1])//不能赋空指针,因为直接c_str会出错, _size(0), _capacity(0)
{_str[0] = '\0';
}

有参(字符串)构造函数 string(const char* str = "") 实现:

//string(const char* str = nullptr)错
//string(const char* str = '\0')错
string(const char* str = "")//常量字符串默认结尾含有\0
//该构造函数可以替代上面无参构造函数:_size(strlen(str))
{_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}

● 拷贝构造函数 string (const string& s) 实现:
 

//s2(s1)
string (const string& s)
{_str = new char[s._capacity+1];//完成深拷贝,+1 存放\0使用strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

1.2 析构函数(destructor)

● ~string()实现:

~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}

1.3 赋值拷贝函数(operator=)

● operator=实现:

	//s2 = s1string& operator=(const string& s){char* tmp = new char[s._capacity + 1];//多开辟的1个空间用来存储\0strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;//支持连续赋值}

2.迭代器(iterators)

● begin实现:

在string类中迭代器中begin表示的就是字符串的首地址的指针,但并不是所有迭代器都是由指针来实现的。

这里使用迭代器的对象有两种,一种是const修饰的,一种是const没有修饰的,所以begin实现应有const修饰,和const不修饰两种:

typedef char* iterator;
typedef const char* const_iterator;iterator begin()
{return _str;
}const_iterator begin() const
{return _str;
}

● end实现:

同理,end实现也一样:

typedef char* iterator;
typedef const char* const_iterator;iterator end()
{return _str + _size;
}const_iterator end() const
{return _str + _size;
}

这里可以再说一下,范围for的实现也是基于迭代器begin和end所实现的,大家可以在汇编代码中就可以看到。

而对于const修饰的对象和const没有修饰的对象分别对应使用的范围for其内部实现也是const修饰的begin,end和const没有修饰的begin,end两种范围for。

3.容量(Capacity)

● size实现:

size_t size() const
{return _size;
}

● capacity实现:

size_t capacity() const
{return _capacity;
}

● resize实现:
由于resize函数的实现可能需要对数组进行扩容,所以我们先实现一下reserve函数:

void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n+1];//多开1个位置给\0strcpy(tmp, _str);delete[] _str;//!!!_str = tmp;_capacity = n;}
}

注意:reserve只有在n大于当前有效空间是才会进行开辟,当n小于当前有效空间不会进行任何操作。

resize实现:

void resize(size n,const char* c)
{if(n>_size){reserve(n);for(size i=size; i<n;i++){_str[i] = c;}_str[n] = '\0';_size = n;}else{_str[n] = '\0'; _size = n;}
}

● clear实现:

注意:clear只是清楚当前所存数据,而不是销毁空间

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

4.元素访问(Element access)

● operator[]实现:

operator[]的实现功能就是访问string类里面字符串的字符所以实现并不复杂:
 

char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}

5.调节器(Modifiers)

● insert实现:

插入字符或字符串那么肯定会涉及到空间不够是否需要扩容的问题,解决了之后剩下的就是将插入位置之后的字符串向后移动插入字符或字符串大小的位置,为要插入的字符或字符串流出插入空间,之后再将字符或字符串插入即可:

void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){//扩容。。。reservereserve(_capacity == 0 ? 4 : _capacity * 2);}//后移字符串方案1//int end = _size;//while (end >= (int)pos)//!!!一个运算符两边操作数类型不同的时候发生类型提升(范围小的像范围大的提升 这里有符号向无符号提升)//{//	_str[end + 1] = _str[end];//	--end;//}//后移字符串方案2size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;_size++;
}void insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (pos < end - len + 1){_str[end] = _str[end - len];--end;}strncpy(_str + pos, str, len);_size += len;
}

● append实现:
既然已经实现了insert,那么我们直接赋用insert实现append,push_back,operator+=即可:

	void append(const char* str){//size_t len = strlen(str);//if (len + _size > _capacity)//{//	//扩容。。。//	reserve(_size + len);//}//strcpy(_str + _size, str);//_size += len;insert(_size, str);}

● push_back实现:

void push_back(const char ch)
{//if (_size == _capacity)//{//	//扩容。。。reserve//	reserve(_capacity == 0 ? 4 : _capacity * 2);//}//_str[_size] = ch;//_str[_size + 1] = '\0';//++_size;insert(_size, ch);
}

● operator+=实现:
 

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

● erase实现:

当len要清除的字符长度大于等于总字符长度减去pos起始位置时就要将pos起始位置后面的字符全都清除,即_str[pos] = '\0'即可。

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

6.字符操作(String operation)

● c_str实现:

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

● find实现:

size_t find(const char c, size_t pos = 0) const
{assert(pos < _size);//从pos位置开始遍历字符串for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;
}size_t find(const char* sub, size_t pos = 0) const
{assert(pos < _size);const char* p = strstr(_str+pos, sub);if (p){return p - _str;//指针-指针为两指针之间的距离}return npos;
}

● substr实现:


与erase分析相似,当要截取的len长度的字符大于等于总字符长度_size减起始位置pos时,就要将pos后面的字符全部截取进行返回:

	string substr(size_t pos = 0, size_t len = npos) const{string sub;if (len >= _size - pos){for (size_t i = pos; i < _size; i++){sub += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){sub += _str[i];}}return sub;}

7.成员常量(Member constant)npos实现:

在标准库中,npos为静态全局变量,在npos文档中就有说明,其值应为size_t类型的-1。

private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;public:static size_t npos;//类内声明
};size_t string::npos = -1;//类外定义

8.非成员函数重载(Non-member function overload)

● swap实现:


在C++库中是有swap函数的,我们可以直接调用使用,但是对于自定义类型来说就比如string类,其直接调用库中的swap函数进行交换的话会调用3次拷贝构造+1次析构(库中的swap实现方式是创建一个新的临时变量tmp进行两个值的交换),这样消耗会很大,所以对于自定义类型我们不推荐直接调用库里面的swap函数。

那么我们该如何实现swap函数呢?我们可以直接对自定义类型里面的内置成员变量进行交换即可满足两者自定义类型的交换,将swap函数定义为非成员函数是因为要满足swap(s1,s2);交换的格式。

内部成员函数:

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

非成员函数(全局函数):

void swap(string& s1, string& s2)
{s1.swap(s2);//直接调用成员函数即可
}

● operator<<实现:

ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;//实现连续打印
}

● operator>>实现:

istream& operator>>(istream& in, string& s)
{s.clear();//输入变量值之前需要将变量原先所存的值给清除char ch;ch = in.get();//可以获取空格' ',cin不支持获取空格while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;//实现连续赋值
}

● getline实现:

istream& getline(istream& in, string& s)
{s.clear();char c;c = in.get();//栈区开辟空间,栈区开辟空间比堆区开辟空间高char ch[128];//提高效率size_t i = 0;while (c != '\n'){ch[i++] = c;if (i == 127){ch[127] = '\0';s += ch;i = 0;}c = in.get();}if (i > 0){ch[i] = '\0';s += ch;}return in;
}

上面所实现的都是string类的常用的一些接口,其他一些没有实现的大家感兴趣的话可以查阅其他资料自行模拟实现一下

二、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤

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

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

相关文章

【Web】第三次

【Web】第三次 1.完成学校官方网站页面制作2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 2.使用动画完成过渡变换效果 1.完成学校官方网站页面制作 html&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://…

Kafka 3.x.x 入门到精通(03)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;03&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.4.1 生产消息的基本步骤2.4.2 生产消息的基本代码2.4.3 发送消息2.4.3.1 拦截器2.4.3.1.1 增加拦截器类2.4.3.1.2 配置拦截器 2.4.3…

毕业答辩PPT怎么做?制作PPT必备的模板网站和AI工具来了!

临近毕业季&#xff0c;眼下应该有不少朋友忙着做论文答辩 PPT&#xff0c;但毕业前也有诸多事项要同时推进&#xff0c;如工作实习、毕业旅游、毕业照筹备等&#xff0c;能花在制作毕设答辩 PPT 上的时间较少&#xff0c;“时间紧任务重”&#xff0c;要想又快又好地搞定答辩 …

【Vision Pro应用】分享一个收集Apple Vision Pro 应用的网站

您是否也觉得 Vision Pro 应用程序商店经常一遍又一遍地展示相同的几个 VisionOS 应用程序?许多有趣、好玩的应用程序似乎消失得无影无踪,让人很难发现它们。为了帮助大家更轻松地探索和体验最新、最有趣的 Vision Pro 应用程序,这里分享一个网站https://www.findvisionapp.…

论文解读:(VPT)Visual Prompt Tuning

文章汇总 要解决的问题 大型模型应用于下游任务本身就存在挑战。最明显的(通常也是最有效的)适应策略是对预先训练好的模型进行全面的端到端微调。 动机 只微调参数的一个子集 解决的办法 只在输入空间中引入少量特定于任务的可学习参数&#xff0c;而在下游训练期间冻结…

Vitis AI 迁移学习并部署在DPU中

目录 1. 本文目的 2. ResNet18介绍 3. 迁移学习 4. 量化配置文件 5. 模型编译&#xff1a; 6. 总结 1. 本文目的 使用迁移学习的方法&#xff0c;将预训练的resnet18模型从原来的1000类分类任务&#xff0c;改造为适应自定义的30类分类任务。 2. ResNet18介绍 ResNet1…

【面试题】java后端开发实习(含答案)

java后端开发实习生-常见面试题 1&#xff09;JDK,JRE,JVM的关系 JDK JRE java开发工具JRE JVM java核心类库 2&#xff09;String类的常用方法 1.关于字符串获取方面 length 获取长度charAt 获取指定索引的字符indexOf 获取字符所在的索引位置lastIndexOf 获取字符所在…

总结一期Jvm

Jvm 数据结构 内存/结构 JVM内存结构主要有三大块&#xff1a;堆内存、方法区和栈。堆内存是JVM中最大的一块内存地址,它主要由年轻代和老年代还有持久代组成,所有new出来的对象都存储在该区域. 栈就是暂存数据的地方,每个线程包含一个栈区&#xff0c;栈存放在一级缓存中&a…

自恋型人格的症状和起因,自恋型人格测试和应对方法

自恋型人格&#xff0c;是一种非常复杂的病态人格&#xff0c;最突出的特点就是对自我价值感的夸大&#xff0c;也就是说&#xff0c;自恋型人格常有一种自大的情绪&#xff0c;习惯外界的赞美&#xff0c;认为自己是独一无二的存在。这种极度的自我膨胀是自恋型人格非常典型的…

Google Ads广告为Demand Gen推出生成式AI工具,可自动生成广告图片

谷歌今天宣布在Google Ads广告中为Demand Gen活动推出新的生成人工智能功能。 这些工具由谷歌人工智能提供支持&#xff0c;广告商只需几个步骤即可使用文本提示创建高质量的图片。 这些由人工智能驱动的创意功能旨在增强视觉叙事能力&#xff0c;帮助品牌在YouTube、YouTube…

数字安全实操AG网址漏洞扫描原理与技术手段分析

在数字化世界的大舞台上&#xff0c;网络安全如同守护者一般&#xff0c;默默保卫着我们的虚拟疆界。当我们在享受互联网带来的便利时&#xff0c;一场无形的战争正在上演。黑客们利用各种手段试图攻破网站的安全防线&#xff0c;而防守方则依靠先进的技术和策略来抵御入侵。其…

云计算时代:SFP、SFP+、SFP28、QSFP+和QSFP28光纤模块详解

随着数据中心的快速发展和云计算的广泛应用&#xff0c;高速、高效率的光纤网络传输成为关键需求。在众多光纤模块中&#xff0c;SFP、SFP、SFP28、QSFP和QSFP28是最常见的几种类型。本文将为您详细解析这几种光纤模块之间的区别&#xff0c;帮助您更好地了解和选择适合自己需求…

网络安全之文件上传漏洞(上篇)(技术进阶)

目录 一&#xff0c;什么是文件上传漏洞&#xff1f;文件上传漏洞会造成什么危害&#xff1f; 二&#xff0c;文件上传靶场upload-labs闯关 Pass-01 Pass-02 Pass-03 Pass-04 Pass-05 Pass-06 Pass-07 ​Pass-08 Pass-09 Pass-10 总结 一&#xff0c;什么是文件上传漏洞&…

Java:优先级队列(堆)

一、初识【堆】 1、什么是【优先级队列】&#xff1f; 前面的文章我们介绍过队列&#xff0c;队列是一种先进先出的数据结构&#xff0c;但是&#xff0c;在某些情况下&#xff0c;操作的数据可能需要有一个优先级来获取数据&#xff0c;例如优先获取队列中最大的元素&#xf…

【linux】动静态库的使用与制作

本章节是基础IO的的最后一个话题!! 目录 浅谈一下动静态库&#xff1a;动静态库的制作与使用&#xff1a;静态库&#xff1a;怎么办&#xff1a;方法一&#xff1a;方法二&#xff1a;方法三&#xff1a;方法四&#xff1a; 是什么&#xff1a;为什么&#xff1a; 动态库&#…

YOLOv8-pose针对视频实时提取打印对应关节点序号及坐标

因为我在找如何提取YOLOv8-pose的关键点的时候&#xff0c;大多都是针对静态图像&#xff0c;视频直接套用不太行&#xff0c;因此就改进了一下&#xff0c;如下&#xff1a; 初步代码&#xff1a; import torch # 导入PyTorch库 import cv2 as cv # 导入OpenCV库并重命名为…

同态加密原理解析

目录 1.数学介绍2.使用多项式环进行加密2.1 私钥和公钥的产生2.2 加密2.3 解密 3.同态计算3.1 同态加法3.2 同态乘法 1.数学介绍 同态加密方案基于一个难以计算的问题Ring Learning with Errorsred。这些方案中的数据在加密和未加密时都用多项式表示。 这里举一个简单的多项式…

主打熟人双向社交,UXLINK 如何用群组打造超强社交生态

社交&#xff0c;作为最强 Web3 流量入口 Web2 世界里&#xff0c;社交产品总是最具想象力。全球使用 Facebook 系列产品的日活用户&#xff08;DAP&#xff09;均值近 30 亿人&#xff0c;占全球人口的 1/3。然而&#xff0c;加密货币用户仅约有 4.2 亿&#xff0c;占全球人口…

C++ 核心编程(1)

c面向对象编程 1.内存分区模型 程序运行前为代码区和全局区。程序运行后才有栈区和堆区。。 1.1 程序运行前 #include<iostream> #include <bits/stdc.h> using namespace std; /*全局区全局变量、静态变量、常量 */ //全局变量 int g_1 20; int g_2 30; //const…

力扣刷题学习(跟随视频学着刷)

使用入门 视频链接 【手把手带你刷Leetcode力扣&#xff5c;各个击破数据结构和算法&#xff5c;大厂面试必备技能【已完结】-哔哩哔哩】 https://b23.tv/vIcRT61 时空复杂度 时间&#xff1a; 空间&#xff1a;主要有O(1)和O(n)两种&#xff0c;只用计算开辟的内存&#xff…