C++修炼之路之string模拟实现

目录

前言

一:构造函数+析构函数+拷贝构造函数

二:c_str +  size  +   capacity  +   operator= +    operator[]

三:普通迭代器 +const迭代器+范围for

四:关系操作符重载 

 五:reserve+resize

六:push_back  +  append  +  operator+=   +

七:insert+erase+swap+find+clear 

八:重载cin和cout 

九:整体代码

 十:总结


接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧

前言

在经过前面的对标准库中string类的各接口函数的简单介绍和使用后,接下来我们就将介绍关于模拟实现一个string类,去更深层次的了解标准库中string的实现,帮助我们更加了解string类,提升代码能力

一:构造函数+析构函数+拷贝构造函数

1.对于模拟实现sting 类的构造函数和拷贝构造函数时,定义的成员变量是const char *str的话,刚好适合拷贝构造函数,但对于后续的resize和reserve开空间这些等就将出现错误,还有使用c_str(c语言str系列函数都会解引用字符串str的)打印字符串时,对于无参构造的string类对象,使用const char *str的话,会直接解引用空指针的,还有const修饰的字符串时不允许修改的,所以要开辟的空间需要我们自己new出来,定义为char *str

2.对于拷贝构造函数我们不写的话,编译器默认实现的是浅拷贝(按照字节一个一个字节的拷贝),对于内置类型浅拷贝是没问题的,但对于自定义类型浅拷贝会出现错误,这里会有两个char*指针指向同一块空间,当一个修改时另一个也为会跟着修改,而且还会析构两次,直接出错,因此就需要我们自己实现深拷贝解决

3.当实现一个无参构造和一个传参构造时,为了简化我们可以考虑实现成一个带缺省值的构造函数

对于这些问题我们更好地实现方式为

	/*string()//无参构造:_str(new char[1]),_size(0),_capacity(0){_str[0] = '\0';}string(const char *str)//传参构造:_size(strlen(str)){_capacity = _size + 1;_str = new char[_capacity+1];strcpy(_str,str);}*///给缺省值的构造//string(const char *str=nullptr)//下面的strlen会进行解引用报错//string(const char *str='\0')//'\0'的ASCII码为0,直接接引用报错//string (const char *str="\0")//字符串的形式,可以string(const char* str = ""):_size(strlen(str)){_capacity = _size==0? 3:_size;//如果_size为0的话,下面开空间_size*2的话还是0会出错_str = new char[_capacity + 1];//开空间strcpy(_str, str);//传数据}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}string(const string& s)//拷贝构造:_size(s._size),_capacity(s._capacity){_str = new char[s._capacity + 1];strcpy(_str, s._str);}
private:char* _str;size_t _size;size_t _capacity;

二:c_str +  size  +   capacity  +   operator= +    operator[]

对于赋值重载可能存在的情况多样(是对已经定义的类对象的操作),主要的有以下几种

对于以上的三种情况下,我们可以分开讨论,但为了代码的不冗余,我们可以优化为直接开辟空间,拷贝数据,释放原空间,在指向新空间 ,但要注意d1=d1这种特殊情况的处理

//不修改成员变量数据的函数,尽量加const
const char* c_str()
{return _str;
}size_t size()const
{return _size;
}
size_t capacity()const
{return _capacity;
}
string& operator=(const string& s)//支持连续赋值
{if (this != &s)//处理d1=d1的情况{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}
const char& operator[](size_t pos)const//const对象的调用
{assert(pos < _size);return _str[pos];
}
char& operator[](size_t pos)//普通对象的调用
{assert(pos < _size);return _str[pos];
}

三:普通迭代器 +const迭代器+范围for

这里的迭代器我们实现为指针的方式,依然是左闭右开的规则,对于范围for其实在底层是转换为迭代器来实现的,所以实现了迭代器也就支持了范围for

const对象要用const迭代器来实现遍历,普通对象要调用普通迭代器或者const迭代器来实现遍历

typedef char* iterator;//普通迭代器
typedef const char* const_iterator;//const迭代器
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str + _size;
}
void Print(const string& s)
{//三种遍历方式for (size_t i = 0; i < s.size(); i++){cout << s[i] << " ";}cout << endl;string::const_iterator it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;for (auto ch : s){cout << ch << " ";}cout << endl;
}
void teststring2()
{string s1;string s2("hello world");s1 = s2;string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";it++;}cout << endl;for (auto ch : s2){cout << ch << " ";}cout << endl;Print(s2);
}

四:关系操作符重载 

bool operator==(const string&s)const
{return strcmp(_str, s._str) == 0;
}
bool operator>(const string& s)const
{return strcmp(_str, s._str) > 0;
}
bool operator >=(const string& s)const
{return (*this == s) || (*this > s);
}
bool operator!=(const string& s)const
{return !(*this == s);
}
bool operator<(const string& s)const
{return !(*this >= s);
}
bool operator<=(const string& s)const
{return !(*this > s);
}

 五:reserve+resize

reserve是开空间,resize是开空间并初始化,但这两个都要注意当开的新空间比capacity小时,不能修改capacity

void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
void resize(size_t n, char ch = '\0')
{if (n < _size)//删除数据,保留前n个{_size = n;_str[_size] = '\0';}else{if (n > _capacity){reserve(n);}size_t i = _size;while (i < n){_str[i] = ch;++i;}_size = n;_str[_size] = '\0';}
}

六:push_back  +  append  +  operator+=   +

在插入数据时需要时刻注意capacity,当capacity不足时,需要及时reserve扩容

void push_back(char ch)
{if (_size + 1 > _capacity){reserve(_capacity*2);}_str[_size] = ch;_size++;_str[_size] = '\0';//insert(_size,ch);
}
void append(const char* s)
{size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, s);_size += len;//insert(_size,s);
}
string& operator+=(char ch)
{push_back(ch);return *this;
}
string& operator+=(const char* str)
{append(str);return *this;
}

七:insert+erase+swap+find+clear 

对于任意位置插入和删除,需要特别关注边界问题,插入需要及时关注capacity,及时扩容

对于插入需要注意size_t 类型会出现的一些问题,当要头插数据时,这时按照以前的方式移动的话,当pos=-1时停止,但size_t 类型的-1是整形最大值,所以要错位移动

string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_size + 1 > _capacity){reserve(_capacity * 2);}//这样的移动会发生错误的/*size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}*/size_t end = _size + 1;while (end >pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;
}
string& inster(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 (end >= pos + len){_str[end] = _str[end - len];--end;}//插入拷贝strncpy(_str + pos, str, len);_size += len;return *this;
}
string& erase(size_t pos, size_t len = npos)
{if (pos == npos || pos+len>=_size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}size_t find(const char* str, size_t pos = 0)
{assert(pos <= _size);char* tmp = strstr(_str + pos, str);if (tmp == nullptr){return npos;}return tmp - _str;
}
void clear()
{_str[0] = '\0';_size = 0;
}

八:重载cin和cout 

ostream& operator<<(ostream& out,const string &str)
{for (auto ch : str){out << ch;}return out;
}
istream& operator>>(istream& in, string& s)
{s.clear();char 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;
}
}

九:整体代码

https://gitee.com/lin-ciyu/cplusplus/tree/master/string2/string2

 十:总结

关于string的模拟实现就到这里了,里面有许多细节需要注意的,多练多去感受

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

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

相关文章

OpenHarmony应用开发引入开源C/C++库---之Har包里的NDK

Har 包 HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C 库、资源和配置文件。通过 HAR 可以实现多个模块或多个工程共享 ArkUI 组件、资源等相关代码。HAR 不同于 HAP&#xff0c;不能独立安装运行在设备上&#xff0c;只能作为应用模块…

百度云加速方法「Cheat Engine」

加速网盘下载 相信经常玩游戏的小伙伴都知道「Cheat Engine」这款游戏内存修改器&#xff0c;它除了能对游戏进行内存扫描、调试、反汇编 之外&#xff0c;还能像变速齿轮那样进行本地加速。 这款专注游戏的修改器&#xff0c;被大神发现竟然还能加速百度网盘资源下载&#xf…

基于RBF的时间序列预测模型matlab代码

整理了基于RBF的时间序列预测模型matlab代码&#xff0c; 包含数据集。采用了四个评价指标R2、MAE、MBE、MAPE对模型的进行评价。RBF模型在数据集上表现非常好。 训练集数据的R2为&#xff1a;0.99463 测试集数据的R2为&#xff1a;0.96973 训练集数据的MAE为&#xff1a;0.…

mongoDB 优化(2)索引

执行计划 语法&#xff1a;1 db.collection_xxx_t.find({"param":"xxxxxxx"}).explain(executionStats) 感觉这篇文章写得很好&#xff0c;可以参考 MongoDB——索引&#xff08;单索引&#xff0c;复合索引&#xff0c;索引创建、使用&#xff09;_mongo…

RuoYi-Vue若依框架-vue前端给对象添加字段

处理两个字段的时候有需求都要显示在下拉框的同一行&#xff0c;这里有两种解决方案&#xff0c;一是后端在实体类添加一个对象&#xff0c;加注解数据库忽略处理&#xff0c;在接口处拼接并传给前端&#xff0c;二是在前端获取的数据数组内为每个对象都添加一个字段&#xff0…

Linux CPU利用率

Linux CPU利用率 在线上服务器观察线上服务运行状态的时候&#xff0c;绝大多数人都是喜欢先用 top 命令看看当前系统的整体 cpu 利用率。例如&#xff0c;随手拿来的一台机器&#xff0c;top 命令显示的利用率信息如下 这个输出结果说简单也简单&#xff0c;说复杂也不是那么…

猫头虎博主深度探索:Amazon Q——2023 re:Invent 大会的 AI 革新之星

摘要 大家好&#xff0c;我是猫头虎博主&#xff01;今天&#xff0c;我要带大家深入了解2023年 re:Invent 大会上发布的一款革命性产品——Amazon Q。让我们一起探索这个引领未来工作方式的新型工具吧&#xff01; 引言 在2023年的 re:Invent 大会上&#xff0c;亚马逊云科…

✌2024/4/4—力扣—盛最多水的容器

代码实现&#xff1a; 方法一&#xff1a;暴力解法——遍历左右边&#xff0c;找出所有面积&#xff0c;取最大值——超时 #define min(a, b) ((a) > (b) ? (b) : (a)) #define max(a, b) ((a) > (b) ? (a) : (b))int maxArea(int *height, int heightSize) {int ans …

SQL注入sqli_labs靶场第五、六题

第五题 根据报错信息&#xff0c;判断为单引号注入 没有发现回显点 方法&#xff1a;布尔盲注&#xff08;太耗时&#xff0c;不推荐使用&#xff09; 1&#xff09;猜解数据库名字&#xff1a;&#xff08;所有ASCII码值范围&#xff1a;0~127&#xff09; ?id1 and length…

论文笔记:面向实体的多模态对齐与融合网络假新闻检测

整理了2022TMM期刊 Entity-Oriented Multi-Modal Alignment and Fusion Network for Fake News Detection&#xff09;论文的阅读笔记 背景模型改进的动态路由算法Cross-Modal Fusion 实验 背景 现有的假新闻方法对多模态特征进行各种跨模态交互和融合&#xff0c;在检测常见假…

使用Ollama在本地运行AI大模型gemma

1.下载&#xff1a; https://github.com/ollama/ollama/releases 2.配置环境变量 我的电脑-右键-属性-系统-高级系统设置-环境变量-【系统环境变量】新建 变量名&#xff1a;OLLAMA_MODELS &#xff08;固定变量名&#xff09; 变量值&#xff1a;E:\Ollama\Lib &#xff0…

Unity自定义icon

Unity自定义icon 1. 新建文件夹 OfficeFabricIconSet2. 新建Iconset3. 新建子文件夹Textures并添加icon图片4. 向iconset添加Quad Icons5. 最终效果 教程来源处&#xff1a; https://365xr.blog/build-your-own-button-icon-set-for-microsoft-hololens-2-apps-with-mrtk-using…

seo调优

SEO 网站地图&#xff1a;sitemap.xmlrobots.txtxxx.com/www.xxx.com 解析到服务器&#xff0c;xxx.com 301 到 www.xxx.comhttps百度站点管理标题描述关键词标签语义化内链外链死链链接html结尾友情链接前端架构 注意&#xff1a;已收录链接&#xff0c;禁止改变链接地址 ro…

Spring boot 入门 ---(一),2024年最新java进阶训练营

spring-snapshots http://repo.spring.io/snapshot spring-milestones http://repo.spring.io/milestone spring-boot-starter-parent是使用Spring Boot的一种不错的方式&#xff0c;但它 并不总是最合适的。有时你可能需要继承一个不同的父POM&#xff0c;或只是不喜欢我…

Linux网络基础 (三) —— Socket

文章目录 Socket 编程基本概念Socket背景Socket 为了解决什么问题 socketsockaddr结构sockaddrsockaddr_insockaddr 和 sockaddr_in 的关系sockaddr_un 示例代码 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alaskan Malamute &#x1f4dc; 博主的代码仓库主页 [ Gitee ]&…

AMRT3D数字孪生引擎

产品概述 AMRT3D引擎是由眸瑞网络科技自主研发、拥有完全自主知识产权的一款全球首款轻量化3D图形引擎&#xff0c;引擎以核心的轻量化技术及AMRT轻量格式为支柱&#xff0c;专为数字孪生项目开发打造。 AMRT3D引擎提供一整套完善的数字孪生解决方案&#xff0c;在数据处理方…

three.js尝试渲染gbl模型成功!(三)

参照教程&#xff1a;https://cloud.tencent.com/developer/article/2276766?areaSource102001.5&traceId88k805RaN_gYngNdKvALJ &#xff08;作者&#xff1a;九仞山&#xff09; 通过最近两天查three.js入门教程了解到 这玩应支持包括 .obj、.gltf等类型的模型结构。 g…

国产低代码工具,轻松搞定数据迁移

在日常的业务系统升级或者数据维护过程中&#xff0c;数据迁移是各个企业用户不得不面临的问题&#xff0c;尤其是数据迁移过程中要保障数据完整性、统一性和及时性&#xff0c;同时也需要注意源数据中的数据质量问题&#xff0c;比如缺失、无效、错误等问题&#xff0c;需要在…

windows版本-idea中下载的java版本在哪

1、点击idea的file-projectStructure 进入&#xff1a; 通过电脑目录进入该目录 找到bin目录&#xff0c;copy该目录地址 copy下来之后设置到系统环境变量中

synchronized 锁升级过程

synchronized 锁升级过程 Java中的synchronized锁升级过程是JVM为了提高并发性能而引入的一种优化策略&#xff0c;它在JDK 1.6及更高版本中得到了显著的改进。以下是synchronized锁从无锁状态到偏向锁、轻量级锁直至重量级锁的详细升级过程&#xff1a; 无锁状态&#xff1a;…