string类模拟实现

目录

模拟实现string类的默认成员函数

模拟实现构造函数

模拟实现拷贝构造函数

模拟实现赋值运算符重载

模拟实现析构函数

string类的增容

模拟实现reserve

 模拟实现resize

string类的遍历和查询

模拟实现const迭代器

模拟实现普通迭代器

模拟实现c_str

模拟实现size 

模拟实现[ ]重载 

模拟实现find,查找字符 

模拟实现find,查找字符串

string类的修改

模拟实现push_back

模拟实现append

 重载+=

模拟实现insert,插入字符

模拟实现insert,插入字符串

string类的删除

模拟实现erase,删除字符或者字符串

模拟实现string类整体代码


在前几期我们学习了库中string类相关函数接口的使用,为了更深层次的理解string类,本期我们需要学习string类的模拟实现。

模拟实现string类的默认成员函数

模拟实现构造函数

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

构造函数,给缺省值的目的是为了让字符串有默认的"\0",使得编译器能够识别字符串的结束。

模拟实现拷贝构造函数

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

创建了对象,交换了中间对象和新创建的对象的成员变量。 

模拟实现赋值运算符重载

string& operator =(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);return *this;}

形参就是一个中间对象,通过实参对象的值传递进行了拷贝构造生成了中间对象。 

模拟实现析构函数

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

对象声明周期结束前先调用析构函数完成资源的清理。资源清理后,编译器再进行对象的销毁,与对象的创建和调用构造函数的顺序刚好相反。 

string类的增容

模拟实现reserve

void reserve(size_t n){if (n > _capacity){//多申请一个空间是为了给'\0'char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}

需要多申请一个空间,是为了留给'\0'。

 模拟实现resize

void resize(size_t n,char ch= '\0'){if (n <= _size){_str[n] = '\0';}else{if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);_size = n;_str[_size] = '\0';}}

resize扩容时有三种情况,扩容的空间小于原来字符串有效字符的个数,扩容的空间大于原来字符的个数但是小于容量,扩容的空间大于原来字符的容量,后两种的实际操作可以分为同一种。

string类的遍历和查询

模拟实现const迭代器

const_iterator begin()const{return _str;}const_iterator end() const{return _str + _size;}

迭代器本质上就是一个指针。 const成员函数const迭代器普通对象和const对象都可以进行访问。

模拟实现普通迭代器

 iterator begin(){return _str;}iterator end(){return _str + _size;}

普通迭代器只有普通对象可以访问。 

模拟实现c_str

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

重载流提取之外的输出字符串的方法。 

模拟实现size 

 size_t size()const{return _size;}

什么时候设置为const成员函数什么时候设置成普通成员函数,这取决于我们对字符串的操作,求大小,打印,这些不需要改变结构的可以设置成const,方便const对象去进行访问。

模拟实现[ ]重载 

    char& operator[] (size_t pos){return _str[pos];}

模拟实现find,查找字符 

size_t find(char ch){for (size_t i = 0; i < _size; ++i){if (ch == _str[i]){return i;}}return npos;}

npos一个静态的无符号const成员变量,表示整型的最大值,再找不到元素时返回npos。

模拟实现find,查找字符串

size_t find(const char* s, size_t pos = 0){const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}}

因为返回的是下标,所以最终通过指针相减获得了对应字符串的首元素的下标,字符串首元素的下标就是整个字符串的下标,因为字符串是连续存储的。

string类的修改

模拟实现push_back

void push_back(char ch){if (_size = _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}

与顺序表类似,因为string类本身就是一个数组,可以理解为它就是一个顺序表。因为刚开始会覆盖'\0',最终一定要在最后一个有效字符的下一位置添加上'\0' 。

模拟实现append

 void append(const char* s){//先得求出要尾插的字符串的长度,因为此时空间不足的话不能以2倍扩容,因为一个字符串的长度可能远远大于容量size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, s);_size += len;}

需要注意,扩容时不能像以往一样二倍扩容,因为字符串的长度是不能预期的。 

 重载+=

	//重载+=,尾插字符
string& operator+=(char ch){push_back(ch);return *this;}//重载+=,尾插字符串
string& operator+=(const char* str){append(str);return *this;}

本质上就是复用了push_back,append两个函数。 

模拟实现insert,插入字符

string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}

插入字符时与顺序表类似,但是需要注意end的位置,因为pos时无符号整型,所以一般情况下要将end的前一个的位置移动到end位置,而不是把end位置移动到end+1位置,这点当无符号整型判断时要注意。

模拟实现insert,插入字符串

string& insert(size_t pos, const char* s){assert(pos <= _size);size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos){_str[end] = _str[end - len];--end;}strncpy(_str + pos, s, len);return *this;}

我们要注意一定要使用strncpy函数控制字符的个数,如果使用strcpy会将'\0'拷贝过去,导致字符串异常结束。

string类的删除

模拟实现erase,删除字符或者字符串

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

要注意pos的范围,因为我们一般删除的都是有效字符,所以pos的范围就是有效字符下标的范围。缺省值是npos,所以如果不给定初始值,默认全部删除完。

模拟实现string类整体代码

namespace yjd
{class string{public://迭代器typedef char* iterator;typedef const char* const_iterator;//构造函数,给缺省值的目的是为了让字符串有默认的"\0",使得编译器能够识别字符串的结束string(const char* str=""):_str(new char[strlen(str) + 1]),_size(0),_capacity(0){strcpy(_str, str);}//拷贝构造函数的深拷贝进阶版本string(string& s):_str(nullptr),_size(s._size),_capacity(s._capacity){string tmp(s._str);std::swap(_str, tmp._str);}//赋值运算符重载的深拷贝进阶版本2string& operator =(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);return *this;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//const迭代器,const对象和普通对象都可以调用调用const_iterator begin()const{return _str;}const_iterator end() const{return _str + _size;}//普通迭代器,普通对象调用iterator begin(){return _str;}iterator end(){return _str + _size;}//以cout<<对象.c_string的方式打印字符串,什么时候设置成const类型的成员变量取决于我们的用法,如果只是打印,对象自然不会更改,所以我们就设置成const成员函数const char* c_string()const{return _str;}//求字符串有效字符的个数size_t size()const{return _size;}//重载[],从而以数组的形式访问字符串的每个元素char& operator[] (size_t pos){return _str[pos];}//进行空间的扩容,实现reserve的功能void reserve(size_t n){if (n > _capacity){//多申请一个空间是为了给'\0'char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}//进行空间的扩容,并且进行初始化,实现resize的功能void resize(size_t n,char ch= '\0'){if (n <= _size){_str[n] = '\0';}else{if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);_size = n;_str[_size] = '\0';}}//尾插一个字符,实现push_back的功能void push_back(char ch){if (_size = _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}//尾插一个字符,实现append的功能void append(const char* s){//先得求出要尾插的字符串的长度,因为此时空间不足的话不能以2倍扩容,因为一个字符串的长度可能远远大于容量size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, s);_size += len;}//重载+=,尾插字符string& operator+=(char ch){push_back(ch);return *this;}//重载+=,尾插字符串string& operator+=(const char* str){append(str);return *this;}//实现find,进行字符的查找size_t find(char ch){for (size_t i = 0; i < _size; ++i){if (ch == _str[i]){return i;}}return npos;}//实现find,查找某个字符串size_t find(const char* s, size_t pos = 0){const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}}//插入元素,实现insertstring& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}//实现insert,插入字符串string& insert(size_t pos, const char* s){assert(pos <= _size);size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos){_str[end] = _str[end - len];--end;}strncpy(_str + pos, s, len);return *this;}//进行字符串的删除,实现erasestring& erase(size_t pos = 0, size_t len = npos){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}private:char* _str;size_t _size;size_t _capacity;//定义静态常变量npos,找不到是就返回nposstatic const size_t npos;};const size_t string::npos = -1;

以上便是string类常见接口的模拟实现,可以帮助大家对string类更深一步的了解,模拟实现不一定要全部掌握,但是库中的string类中的相关常见接口一定要掌握。

本期内容到此结束^_^

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

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

相关文章

Java面向对象(初级)

面向对象编程(基础) 面向对象编程&#xff08;OOP&#xff09;是一种编程范式&#xff0c;它强调程序设计是围绕对象、类和方法构建的。在面向对象编程中&#xff0c;程序被组织为一组对象&#xff0c;这些对象可以互相传递消息。面向对象编程的核心概念包括封装、继承和多态。…

sqlite3 数据库加密:sqlite3mc

命令简单使用&#xff1a; // 加密&#xff0c;将 test.db 加密后导出新的数据库 test_enc.db user > sqlite3mc test.db sqlite> ATTACH DATABASE test_enc.db AS test_enc KEY xxxxxxxx; --设置密码 sqlite> SELECT sqlcipher_export(test_enc); sqlite> DETACH…

Vue3选项式API和组合式API详解

前言 相信学习Vue3的人中大多数都是之前使用Vue2开发的&#xff0c;当拿到一个Vue3项目时就接触到了组合式api&#xff0c;但对于组合式api不了解的人第一眼看上去会觉得一头雾水。&#xff1a;“什么玩意&#xff0c;乱七八糟的&#xff0c;选项式api多好&#xff0c;方法变量…

Vue3揭秘:案例深度讲解Vue3全部新特性

🧙‍♂️ 诸位好,吾乃诸葛妙计,编程界之翘楚,代码之大师。算法如流水,逻辑如棋局。 📜 吾之笔记,内含诸般技术之秘诀。吾欲以此笔记,传授编程之道,助汝解技术难题。 📄 吾之文章,不以繁复之言,惑汝耳目;但以浅显之语,引汝入胜。 🚀 若此文对阁下有所裨益,敬…

python简单分割文件的方法(python经典案例)

在某些情况下&#xff0c;我们需要将一个大文件分割成多个小文件&#xff0c;或者根据长度、行数等规则将一个文件分割成多个文件。Python提供了简单的方式来实现这些操作。 方法1&#xff1a;使用seek和read方法 下面是一段示例代码&#xff0c;它将一个文件分割成5个小文件…

Linux bridge开启hairpin模拟测试macvlan vepa模式

看到网上介绍可以通过Linux bridge 开启hairpin方式测试macvlan vepa模式&#xff0c;但是没有找到详细资料。我尝试测试总提示错误信息&#xff0c;无法实现&#xff0c;经过几天的研究&#xff0c;我总算实现模拟测试&#xff0c;记录如下&#xff1a; 参考 1.Linux Macvla…

「Vue3面试系列」Vue3.0性能提升主要是通过哪几方面体现的?

文章目录 一、编译阶段diff算法优化静态提升事件监听缓存SSR优化 二、源码体积三、响应式系统参考文献 一、编译阶段 回顾Vue2&#xff0c;我们知道每个组件实例都对应一个 watcher 实例&#xff0c;它会在组件渲染的过程中把用到的数据property记录为依赖&#xff0c;当依赖发…

MicroPython的交互式解释器模式 REPL

MicroPython的交互式解释器模式又名REPL&#xff08;read-eval-print-loop&#xff09;&#xff0c;就是一种命令输入交互模式&#xff0c;跟Python的REPL是类似的&#xff0c;就是在命令行直接输入Python代码或表达式执行并打印结果。关于MicroPython的REPL跟通常的Python类似…

linux运维面试题

linux运维面试题 面试 K8S篇(高可用) Q&#xff1a;k8s是什么&#xff1f;架构&#xff1f; Kubenetes是一个开源的容器集群管理系统。主要用于容器编排&#xff0c;解决容器调度问题。当应用请求时&#xff0c;k8s需要合理分配请求到空闲node节点上去。k8s使用的主从模式&…

hive sql常用函数

目录 一、数据类型 二、基础运算 三、字符串函数 1、字符串长度函数: length() 2、字符串反转函数&#xff1a;reverse 3、字符串连接函数 4、字符串截取函数 5、字符串分割函数&#xff1a;split 6、字符串查找函数 7、ascii 8、base64 9、character_length 10、c…

python通过JS逆向采集艺恩电影数据, 并制作可视化

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 环境使用: 版 本&#xff1a; python 3.10 编辑器&#xff1a;pycharm 2022.3.2 nodejs 模块使用: requests -> pip install requests execjs -> pip install…

【Git】在 IDEA 中合并多个 commit 为一个

文章目录 1 未提交到远程分支1.1 需求说明1.2 reset 操作1.3 再次 push 2 已经提交到远程分支2.1 需求说明2.2 rebase 操作2.3 强制 push 分两种情况&#xff1a; 一种是本地提交还没推到远程&#xff0c;这种好处理另一种是已经提交到远程分支&#xff0c;这个略麻烦 1 未提…

【接口测试】Postman(三)-变量与集合

一、变量 ​ 变量这个概念相信大家都不陌生&#xff0c;因此在这里我们不介绍了。主要说一下在Postman中有哪几类变量&#xff0c;主要包括以下四类&#xff1a; Global&#xff08;全局&#xff09; Environment&#xff08;环境&#xff09; Local&#xff08;本地&#xf…

Linux中安装Maven3.6.1

一、安装及配置maven 1.下载maven安装包 首先需要切换到自己需要安装的目录 我自己是把配置都放到了&#xff1a;/usr/local/maven路径下 cd /usr/local/maven 下载maven安装包&#xff1a; wget https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven…

FreeRTOS信号量学习

目录 一、信号量的特性 1. 信号量的常规操作 2. 信号量跟队列的对比 3. 两种信号量的对比 4. 信号量函数 4.1 创建 4.2 删除 4.3 give/take 5. 使用二进制信号量来同步 6. 防止数据丢失 7. 使用计数型信号量 队列(queue)可以用于传输数据&#xff1a;在任务之间、任务和…

Linux多线程:线程池(单例),读写锁

目录 一、线程池&#xff08;单例模式&#xff09;1.1 makefile1.2 LockGuard.hpp1.3 log.hpp1.4 Task.hpp1.5 Thread.hpp1.6 ThreadPool.hpp1.7 main.cc 二、STL,智能指针和线程安全2.1 STL中的容器是否是线程安全的?2.2 智能指针是否是线程安全的? 三、其他常见的各种锁四、…

dockerfile ENTRYPOINT 执行.sh脚本提示找不到文件或文件不存在 No such file or directory

我这里记录的是我遇到的一种特殊情况&#xff0c;如果你也遇到了这个问题&#xff0c;且都试了在百度中找到的解决方法还没有解决可以看看是不是和我遇到的问题一样。 在Dockerfile中&#xff0c;我ADD了两个文件&#xff0c;一个是jar包&#xff0c;一个是一个执行jar包的.sh…

proto与json的互相转换

proto与json的互相转换 proto使用proto python dict和messagepython message序列化golangmessage序列化message转json proto使用 生成逻辑请参考 https://blog.csdn.net/qq_43645782/article/details/127112663 proto syntax "proto3";message testRequest {stri…

微服务之配置中心与服务跟踪

zookeeper 配置中心 实现的架构图如下所示&#xff0c;采取数据加载到内存方式解决高效获取的问题&#xff0c;借助 zookeeper 的节点监听机制来实现实时感知。 配置中心数据分类 事件调度&#xff08;kafka&#xff09; 消息服务和事件的统一调度&#xff0c;常用用 kafka …

c语言突击函数

函数 1.函数&#xff1a;是具有一定功能的程序块&#xff0c;是c语言的基本组成单位 2.函数的定义&#xff1a;[函数类型] 函数名 &#xff08;形式参数&#xff09; 函数不可以嵌套定义&#xff0c;但是可以嵌套调用 3.函数名缺省返回值&#xff0c;默认int&#xff1b; 4…