【C++】学习笔记——string_4

文章目录

  • 六、string类
    • 7. string类的模拟实现
  • 未完待续


六、string类

7. string类的模拟实现

我们在上文简单实现了string类的构造函数。不知道大家有没有发现一个问题,我们在进行实现无参的构造函数时,初始化列表将 _str 初始化为 nullptr 了,但是这里真的可以初始化为 nullptr 吗?要仔细想一想,当不提供参数的时候,我们是应该得到一个空指针还是一个空串?对,我们应该是得到一个空串,空串末尾是携带 ‘\0’ 的。我们在修改上次的问题的同时,再将两个函数给合并成一个缺省函数。

// string.h 头文件下
#pragma once
#include<iostream>namespace my
{class string{public:// 默认是空串而不是空指针string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}private:char* _str;size_t _size;size_t _capacity;};
}

OK,接下来实现析构函数

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

析构函数没什么好说的,接下来我们来实现一下字符串的遍历
首先要实现的就是 size 函数了,遍历需要知道字符串的长度。

// 加 const 使其成为 const 成员函数,使 const 对象也能调用这个函数
size_t size() const
{return _size;
}

遍历方法①:下标 + [] 遍历

// 引用返回,可读可写
inline char& operator[](size_t pos)
{// 越界检查// assert函数需要: #include<assert.h>assert(pos < _size);// _str是字符数组return _str[pos];
}

写了这么多了,我们来使用一下。

// test.cpp 源文件下
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");for (size_t i = 0; i < s.size(); ++i){std::cout << s[i];}std::cout << std::endl;return 0;
}

在这里插入图片描述
nice!但是,我们要知道,下标访问支持可读可写,但是const对象不支持,所以刚刚那个函数不支持const对象,于是我们得重载一个针对于const对象的下标访问函数。

// 针对 const对象 的可读不可写,加 & 是为了减少拷贝
inline const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}

访问容量函数:

size_t capacity() const
{return _capacity;
}

遍历方法②:迭代器。我们之前介绍了,迭代器是类似指针的东西,但是它不一定是指针! 但是我们这里可以就把它当作指针来实现。

// string.h 头文件下
#pragma once
#include<iostream>
#include<assert.h>namespace my
{class string{public:// 底层是原生指针的迭代器typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}// 默认是空串而不是空指针string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// 加 const 使其成为 const 成员函数,使 const 对象也能调用这个函数size_t size() const{return _size;}// 引用返回,可读可写inline char& operator[](size_t pos){assert(pos < _size);return _str[pos];}// 针对 const对象 的可读不可写,加 & 是为了减少拷贝inline const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}size_t capacity() const{return _capacity;}private:char* _str;size_t _size;size_t _capacity;};
}

来跑一跑:

// test.cpp源文件下
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");//for (size_t i = 0; i < s.size(); ++i)//{//	std::cout << s[i];//}//std::cout << std::endl;for (auto e : s){std::cout << e;}std::cout << std::endl;return 0;
}

由于 范围for 底层就是替换成迭代器的形式,所以这里使用 范围for 来验证迭代器的实现。
在这里插入图片描述
没问题,接下来就是为 const对象 来实现专门的 const_iterator 了。

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

反向迭代器咱们就不去实现了,咱们学到后面了在谈。
接下来实现 string类 的修改。①push_back 尾插。首先先实现扩容函数 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;}
}
void push_back(char ch)
{if (_size == _capacity){// 扩容2倍reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}

接下来是②append追加函数。

void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}// 从末尾开始,拷贝新字符串strcpy(_str + _size, str);_size += len;
}

接下来是③重载 +=

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

由于我们还没有实现流插入和流提取函数,所以我们无法直接输出我们的字符串,但是我们可以实现 c_str() 函数,这个函数的作用是将 string类型 的字符串转换成C语言的字符数组。

char* c_str()
{// 返回字符数组就好return _str;
}

再来跑跑:

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");//for (size_t i = 0; i < s.size(); ++i)//{//	std::cout << s[i];//}//std::cout << std::endl;/*for (auto e : s){std::cout << e;}std::cout << std::endl;*/s += '!';s += "YYYYYYYYY";std::cout << s.c_str() << std::endl;return 0;
}

在这里插入图片描述
inserterase

// 在 pos 位置插入一个字符
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){// 扩容2倍reserve(_capacity == 0 ? 4 : 2 * _capacity);}// 小细节size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;
}

注意细节:end 选择的是每次右移的移动终点,如果是起点,会写成 end = _size; end >= pospos 等于 0 时,由于end和pos都是无符号整形,end不可能比 0 小,所以就会死循环,当end处于右侧时则完美解决了这个问题。

// 从 pos 开始,删除 len 个字符,如果 len 是 npos ,则全删
void erase(size_t pos, size_t len = npos)
{assert(pos < _size);// pos + len >= _size 可能会溢出if (len == npos || len >= _size - pos){_str[pos] = '\0';_size = pos;}strcpy(_str + pos + len, _str + pos);_size -= len;
}

跑跑看:

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");//for (size_t i = 0; i < s.size(); ++i)//{//	std::cout << s[i];//}//std::cout << std::endl;/*for (auto e : s){std::cout << e;}std::cout << std::endl;*///s += '!';//s += "YYYYYYYYY";//std::cout << s.c_str() << std::endl;s.insert(5, 'T');std::cout << s.c_str() << std::endl;s.erase(8);std::cout << s.c_str() << std::endl;return 0;
}

在这里插入图片描述

接下来是 resize

void resize(size_t n, char ch = '\0')
{// 比 size 小则删除if (n <= _size){_str[n] = '\0';_size = n;}else{// 比size大则填充reserve(n);for (size_t i = _size; i < n; ++i){_str[i] = ch;}_str[n] = '\0';_size = n;}
}

再看看:

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");//for (size_t i = 0; i < s.size(); ++i)//{//	std::cout << s[i];//}//std::cout << std::endl;/*for (auto e : s){std::cout << e;}std::cout << std::endl;*///s += '!';//s += "YYYYYYYYY";//std::cout << s.c_str() << std::endl;//s.insert(5, 'T');//std::cout << s.c_str() << std::endl;//s.erase(8);//std::cout << s.c_str() << std::endl;s.resize(5);std::cout << s.c_str() << std::endl;s.resize(20, 'Q');std::cout << s.c_str() << std::endl;return 0;
}

在这里插入图片描述
写了这么多成员函数,但是没有写拷贝构造函数。有人可能会说哈,拷贝构造函数是默认成员函数,不写编译器也会自动生成,我们不需要写,但是真的不需要写吗?我们之前说过,编译器默认生成的拷贝构造都是浅拷贝,按字节一个一个拷贝,我们来看看下图:

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace my;int main()
{string s("hello,world");// 拷贝构造string s2(s);return 0;
}

在这里插入图片描述
我们发现这里拷贝构造出来的 s2s_str 指向同一块空间,这就是浅拷贝导致的,这样的情况会使:操作其中一个,另一个也会改变,同一块空间会被析构两次,产生报错。所以说默认的拷贝构造函数不一定好,我们需要自己实现一个深拷贝的拷贝构造函数。

string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

在这里插入图片描述
不一样了。


未完待续

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

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

相关文章

Unity LensFlare 入门

概述 在项目的制作过程中&#xff0c;太阳光的使用一定是不可缺少的部分&#xff0c;但是如果想实现真实太阳光眼睛看到的镜头炫光效果&#xff0c;那这部分的内容一定不要错过喔&#xff0c;接下来让我们来学习这部分的内容吧&#xff01; Hale(光环效果) Color&#xff1a;…

操作系统:线程

目录 前言&#xff1a; 1.线程 1.1.初识线程 1.2.“轻量化”进程 1.3.线程与进程 2.线程控制 2.1.pthread原生线程库 2.2.线程控制的接口 2.2.1.线程创建 2.2.线程退出|线程等待|线程分离|线程取消 2.3.pthread库的原理 2.4.语言和pthread库的关系 2.5.线程局部…

数据可视化宝典:Matplotlib图形实战

在数据分析领域&#xff0c;图形化展示数据是非常重要的环节。Python中的matplotlib库是绘制各类图形的强大工具。本文将介绍如何使用matplotlib绘制折线图、直方图、饼图、散点图和柱状图等数据分析中常见的图形&#xff0c;并附上相应的代码示例&#xff0c;可以当初matplotl…

模型智能体开发之metagpt-单智能体实践

需求分析 根据诉求完成函数代码的编写&#xff0c;并实现测试case&#xff0c;输出代码 代码实现 定义写代码的action action是动作的逻辑抽象&#xff0c;通过将预设的prompt传入llm&#xff0c;来获取输出&#xff0c;并对输出进行格式化 具体的实现如下 定义prompt模版 …

神经网络与深度学习--网络优化与正则化

文章目录 前言一、网络优化1.1网络结构多样性1.2高维变量的非凸优化1.鞍点2.平坦最小值3.局部最小解的等价性 1.3.改善方法 二、优化算法2.1小批量梯度下降法&#xff08;Min-Batch&#xff09;2.2批量大小选择2.3学习率调整1.学习率衰减&#xff08;学习率退火&#xff09;分段…

Android数据恢复软件快速比较:Android数据恢复的7最佳工具

您在 Android 设备上保留哪些类型的数据&#xff1f;如果您和大多数人一样&#xff0c;那么您可能已经列出了文档、照片、视频和音频文件。如果您使用智能手机或平板电脑的时间足够长&#xff0c;我们愿意打赌您拥有Android数据丢失的第一手经验。 幸运的是&#xff0c;我们也…

打破失联困境:门店如何利用AI智能名片B2B2C商城小程序重构与消费者的紧密连接?

在如今这个消费者行为日益碎片化的时代&#xff0c;门店经营者们时常感叹&#xff1a;消费者进店如同一场不期而遇的缘分&#xff0c;然而一旦离开门店&#xff0c;就仿佛消失在茫茫人海中&#xff0c;难以再觅其踪迹。这种“进店靠缘分&#xff0c;离店就失联”的困境&#xf…

Cisco IOS XE Web UI 权限提升漏洞复现(CVE-2023-20198)

0x01 产品简介 Web UI 是一种基于GUI的嵌入式系统管理工具,能够提供系统配置、简化系统部署和可管理性以及增强用户体验。它带有默认映像,因此无需在系统上启用任何内容或安装任何许可证。Web UI 可用于构建配置以及监控系统和排除系统故障,而无需CLI专业知识。 0x02 漏洞…

Codeforces Round 941 (Div. 2) D. Missing Subsequence Sum

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习三

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

java-spring-mvc(服务端接收客户端传参)

目录 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 ✨ 声明参数接收 ✨声明pojo类来接收 &#x1f52a;小试牛刀 &#x1f3af; 服务端接收参数 ✨HttpServletRequest接收 HttpServletRequest是Java Servlet规范中定义的一个接口&#xff0c;它提供了与HTTP请求…

解决Redis的键值前出现类似\xAC\xED\x00\x05t\x00*这样的字符序列

文章目录 1.问题2.解决方法3.StringRedisTemplate和RedisTemplate的区别 1.问题 在使用RedisTemplate对Redis进行操作时,发现Reids键值对前有\xAC\xED\x00\x05t\x00*这样的字符序列 如图所示: 虽说不影响使用,但是听影响观感的 2.解决方法 查找了很多方法,可以指定RedisTem…

用户中心(下)

文章目录 计划登录逻辑接口简单说明cookie和session写代码流程后端逻辑层控制层测试用户管理接口 前端简化代码对接后端代理 计划 开发完成后端登录功能 &#xff08;单机登录 > 后续改造为分布式 / 第三方登录&#xff09;✔开发后端用户的管理接口 &#xff08;用户的查询…

基于EO平衡优化器算法的目标函数最优值求解matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于EO平衡优化器算法的目标函数最优值求解matlab仿真。提供九个测试函数&#xff0c;分别对九个测试函数仿真输出最优解以及对应的优化收敛曲线。 2.测试软件版…

树莓派点亮LED灯

简介 使用GPIO Zero library 的 Python库实现点亮LED灯。接线 树莓派引脚参考图如下&#xff1a; LED正极 接GPIO17 LED负极 接GND 权限 将你的用户加到gpio组中&#xff0c; 否则无法控制GPIO sudo usermod -a -G gpio 代码 from gpiozero import LED from time impor…

ES全文检索支持拼音和繁简检索

ES全文检索支持拼音和繁简检索 1. 实现目标2. 引入pinyin插件2.1 编译 elasticsearch-analysis-pinyin 插件2.2 安装拼音插件 3. 引入ik分词器插件3.1 已有作者编译后的包文件3.2 只有源代码的版本3.3 安装ik分词插件 4. 建立es索引5.测试检索6. 繁简转换 1. 实现目标 ES检索时…

Springboot+Vue+小程序+基于微信小程序护农远程看护系统

开发平台为idea&#xff0c;maven管理工具&#xff0c;Mybatis操作数据库&#xff0c;根据市场数字化需要为农户打造小程序可远程查看农场的种植情况。项目是调试&#xff0c;讲解服务均可有偿获取&#xff0c;需要可在最下方QQ二维码处联系我。 SpringbootVue小程序&#xff…

【UE5】数字人基础

这里主要记录一下自己在实现数字人得过程中涉及导XSens惯性动捕&#xff0c;视频动捕&#xff0c;LiveLinkFace表捕&#xff0c;GRoom物理头发等。 一、导入骨骼网格体 骨骼网格体即模型要在模型雕刻阶段就要雕刻好表捕所需的表情体(blendshape)&#xff0c;后面表捕的效果直…

API安全

一&#xff0c;什么是API API指的是应用程序编程接口&#xff08;Application Programming Interface&#xff09;&#xff0c;是一组定义了软件组件如何相互交互的规范。通过API&#xff0c;不同的软件可以相互通信和交换数据&#xff0c;实现不同软件之间的集成和互操作。 …

代码随想录算法训练营DAY45|C++动态规划Part7|70.爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数

文章目录 70.爬楼梯&#xff08;进阶版&#xff09;322. 零钱兑换思路CPP代码 279.完全平方数思路CPP代码 70.爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯 文章讲解&#xff1a;70.爬楼梯(进阶版) 322. 零钱兑换 力扣题目链接 文章讲解&#xff1a;322…