【C++修行之路】STL——模拟实现string类
文章目录
- 前言
- 类框架
- 构造与析构
- c_str
- 迭代器
- 操作符重载
- []:
- =:
- > == >= < <= !=:
- reverse与resize
- reverse
- resize
- push_back与append
- 复用实现+=
- insert和erase
- c_str与流插入、流提取
- erase
- swap(s1,s2)与s1.swap(s2)
- 结语
前言
这次我们分几个部分来实现string类。具体请看目录。
说明:模拟实现只实现了string中最常用的功能。
类框架
首先我们要与库里的string类区分,因此我们定义一个命名空间,名字可以随意起,这里教学因此命名为Teacher
string.h:
namespace Teacher
{class string{public://函数实现private:char* _str;int _size;int _capacity;};
}
约定:在我们模拟实现过程中,_str存储字符串内容,_size表示现在字符串的大小(不包括\0)_capacity表示字符串一共有多大空间(不包括\0)
构造与析构
空字符串可以直接用缺省值处理,我们不必再写一个空参构造函数。
//函数实现
string(const char* str = "") : _size(strlen(str))
{_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}~string()
{delete[] _str;_str = nullptr;_capacity = _size = 0;
}
c_str
迭代器
用指针来模拟实现一下迭代器,唯一需要注意的就是一定要写成iterator和const_iterator,不然在使用范围for的时候会报错。
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str + _size;
}
操作符重载
[]:
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}
=:
赋值时,由于我们不知道子母串大小情况,所以我们把要覆盖的串直接清空,再把新的内容腾上去,但这又涉及一个问题,如果清空后覆盖失败怎么办?这样我们不仅没有拷贝成功,还失去了原有的串。因此我们采用一个临时数组先将我们的内容拷贝到这个临时串上,再清理原来的串。
代码如下:
string& operator=(const string& s)
{if (this != &s){_size = s._size;_capacity = s._capacity;char* tmp = new char[_capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;}return *this;
}
> == >= < <= !=:
由于和我们实现日期类的基本思路一致,我们按照原来的思路书写即可:
bool operator>(const string& s) const
{return strcmp(_str, s._str);
}
bool operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{return _str > s._str || _str == s._str;
}
bool operator<(const string& s) const
{return !(_str >= s._str);
}
bool operator<=(const string& s) const
{return !(_str > s._str);
}
bool operator!=(const string& s) const
{return !(_str == s._str);
}
reverse与resize
reverse
当我们对string对象进行增加操作时(不管是追加一个字符还是追加一个串)我们都需要判断当前对象的容量还够不够,如果不够,我们就要按需要扩容。至于啥时候需要扩容,我们在需要的函数里再判断。reverse只管扩容。
void reserve(size_t newsize)
{char* tmp = new char[newsize + 1];//按照我们的约定容量不包括\0,因此我们在这里加上strcpy(tmp,_str);delete[] _str;_str = tmp;_capacity = newsize;
}
resize
没什么好说的,注意缺省值是如何使用的即可
void resize(size_t n, char ch = '\0')
{if (n < _size){// 删除数据--保留前n个_size = n;_str[_size] = '\0';}else if (n > _size){if (n > _capacity){reserve(n);}size_t i = _size;while (i < n){_str[i] = ch;++i;}_size = n;_str[_size] = '\0';}
}
push_back与append
实现了reverse函数之后,我们就可以实现push_back和append函数了
push_back:
void push_back(char ch)
{if (_size + 1 > _capacity){reserve(_size * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';
}
append:
void append(const char* str)
{int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;
}
如果你对其中判断是否需要扩容感到疑惑,建议你再想想我们之前对_size和_capacity的约定。
复用实现+=
因为我们已经写好了上面的接口,直接复用即可。
string& operator+=(const char ch)
{push_back(ch);
}
string& operator+=(const char* str)
{append(str);
}
insert和erase
insert:可以在任意位置插入
在这里提供两个思路,但由于一些边界问题,第一个思路你要考虑判断坐标是否合法。
void insert(size_t pos, const char ch)
{//在pos处插入一个字节if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size;//无符号数会出越界的bugwhile (end >= pos && end != -1){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;
}void insert2(size_t pos, const char ch)
{//在pos处插入一个字节if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size+1;while (end > pos){_str[end] = _str[end-1];end--;}_str[pos] = ch;_size++;
}
erase:删除一部分数据,注意这里给出了npos的缺省值,我们在类成员函数处加上即可。
static const size_t npos;const size_t npos = -1;
上面这种给静态变量赋值的方式只能赋值成int,其他类型均不可以。
void erase(size_t pos, size_t len = npos)
{int begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;
}
c_str与流插入、流提取
由于自定义的类型不能直接输出打印,因此我们要拿到对象内的字符数组,这样才能按照C语言的方式来打印字符串。
代码如下:
const char* c_str()
{return _str;
}
而流插入和流提取即重载两个操作符:
为什么需要重载流插入和流提取?
流提取:
ostream& operator<<(ostream& out, const string& s)
{//需要写迭代器for (auto ch : s){out << s;}return out;
}
流插入:
istream operator>>(istream& is,string& s)
{char ch = in.get()char buff[128];size_t i = 0;while(ch != ' ' && ch != '\n'){buff[i] = ch;i++;if(i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if(i != 0){buff[i] = '\0';s+=buff;}
}
erase
erase可以分两种情况考虑,即把pos后面全删了,还是删除pos后有限个元素,我们可以单独处理,具体详见代码:
void erase(size_t pos,size_t len = npos)
{if (pos + len >= _size || len == npos){_str[pos] = '\0';_size = pos;}else{strcpy(_str+pos,_str+pos+len);_size -= len;}
}
因为设计到在同一个字符串里用strcpy,我们这里不用担心会覆盖的问题,因为左边是我们要删除的,右边的是我们的源头,所以覆盖的是无用的元素。可以直接使用。
swap(s1,s2)与s1.swap(s2)
最后来谈一下交换两个对象的函数。
更推荐使用第二种,
void swap(string& s2)
{std::swap(_str, s2._str);std::swap(_size, s2._size);std::swap(_capacity, s2._capacity);
}
如果选择第一种会怎么样?其实会进行三次拷贝构造,这样是非常低效的,详细如图,采用第二种会好很多。1
结语
到这里,本篇文章就到此为止了,我们下次见~
相关文章:

【C++修行之路】STL——模拟实现string类
文章目录前言类框架构造与析构c_str迭代器操作符重载[]::> > < < !:reverse与resizereverseresizepush_back与append复用实现insert和erasec_str与流插入、流提取eraseswap(s1,s2)与s1.swap(s2)结语前言 这次我们分几个部分来实现string类…...

CorelDRAW2023最新版序列号使用教程
CorelDRAW2023用起来非常顺手,旨在为用户解决因在工作上带来的问题,在业内可谓享有极高的声誉,是业内人士常用的一款工具,有了它,可以更好的帮助用户把握好各个方面的细节,减少其他方面的失误,让…...
【一天一门编程语言】Python 语言程序设计极简教程
文章目录 Python 语言程序设计极简教程一、Python语言简介1.1 Python的优势1.2 Python的应用二、Python基础语法2.1 Python基础2.1.1 注释2.1.2 变量2.1.3 运算符2.1.4 控制流2.1.5 函数2.2 Python数据类型2.2.1 数字2.2.2 字符串2.2.3 列表2.2.4 元组2.2.4.1 元组的基本操作创…...

14、KL散度
KL 散度,是一个用来衡量两个概率分布的相似性的一个度量指标。 现实世界里的任何观察都可以看成表示成信息和数据,一般来说,我们无法获取数据的总体,我们只能拿到数据的部分样本,根据数据的部分样本,我们会…...

TypeError: load() missing 1 required positional argument: ‘Loader‘解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…...

【设计模式】 观察者模式介绍及C代码实现
【设计模式】 观察者模式介绍及C代码实现 背景 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”,即一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。…...

01-Maven基础-简介安装、基本使用(命令)、IDEA配置、(写jar,刷新自动下载)、依赖管理
文章目录0、Maven1、Maven 简介2、Maven 安装配置安装配置步骤3、Maven 基本使用Maven 常用命令Maven 生命周期IDEA 配置 MavenMaven 坐标详解IDEA 创建 Maven 项目IDEA 导入 Maven 项目配置 Maven-Helper 插件 (非常实用的小插件)依赖管理使用坐标导入 jar 包依赖范围0、Maven…...

一、前端稳定性规约该如何制定
前言 稳定性是数学或工程上的用语,判别一系统在有界的输入是否也产生有界的输出。若是,称系统为稳定;若否,则称系统为不稳定。 前端稳定性的体系建设大约可以分为了发布前,发布后,以及事故解决后三个阶段…...
Docker(三)Docker网络
目录1 结论知识2 link3 自定义网络1 结论知识 每一个容器启动时都会被分配一个ip地址;宿主机可以ping通任何一个docker容器;启动docker之后,宿主机默认网卡docker0,启动容器在宿主机注册网卡,使用的evth-pair技术&…...

Js高级API
Decorator装饰器 针对属性 / 方法的装饰器 // decorator 外部可以包装一个函数,函数可以带参数function Decorator (type) {/*** 这里是真正的decorator* description: 装饰的对象的描述对象* target:装饰的属性所述类的原型,不是实例后的类。如果装饰…...

团队:在人身上,你到底愿意花多大精力?
你好,我是叶芊。 今天我们讨论怎么带团队这个话题,哎先别急着走,你可能跟很多人一样,觉得带团队离我还太远,或者觉得我才不要做管理,我要一路技术走到底,但是你知道吗?带团队做事&am…...
Linux-Poolkit提权
Linux-Poolkit提权 漏洞复现- Linux Polkit 权限提升漏洞(CVE-2021-4034) 0x00 前言 polkit是一个授权管理器,其系统架构由授权和身份验证代理组成,pkexec是其中polkit的其中一个工具,他的作用有点类似于sudo&#x…...
【React全家桶】React Hooks
React Hookshooks介绍useState(保存组件状态)useEffect()useCallback(记忆函数)useMemo() 记忆组件useRef(保存引用值)useReducer()useContext(减少组件层级)自定义hookshooks介绍 在react类组件(class)写法中,有setState和生命周期对状态进…...

CLIP论文阅读
Learning Transferable Visual Models From Natural Language Supervision 利用自然语言的监督信号学习可迁移的视觉模型 概述 迁移学习方式就是先在一个较大规模的数据集如ImageNet上预训练,然后在具体的下游任务上再进行微调。这里的预训练是基于有监督训练的&am…...
华为OD机试真题Python实现【身高排序】真题+解题思路+代码(20222023)
身高排序 题目 小明今年升学到了小学一年级, 来到新班级后,发现其他小朋友身高参差不齐, 然后就想基于各小朋友和自己的身高差,对他们进行排序, 请帮他实现排序 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Python)真题目录汇总 输入 第一行为正整数H…...

Spring Cache的使用--快速上手篇
系列文章目录 分页查询–Java项目实战篇 全局异常处理–Java实战项目篇 完善登录功能–过滤器的使用 更多该系列文章请查看我的主页哦 文章目录系列文章目录前言一、Spring Cache介绍二、Spring Cache的使用1. 导入依赖2. 配置信息3. 在启动类上添加注解4. 添加注解4.1 CacheP…...
(三十八)MySQL是如何支持4种事务隔离级别的?Spring事务注解是如何设置的?
上次我们讲完了SQL标准下的4种事务隔离级别,平时比较多用的就是RC和RR两种级别,那么在MySQL中也是支持那4种隔离级别的,基本的语义都是差不多的 但是要注意的一点是,MySQL默认设置的事务隔离级别,都是RR级别的&#x…...
【博学谷学习记录】大数据课程-学习第八周总结
Hadoop初体验 使用HDFS 1.从Linux本地上传一个文本文件到hdfs的/目录下 #在/export/data/目录中创建a.txt文件,并写入数据 cd /export/data/ touch a.txt echo "hello" > a.txt #将a.txt上传到HDFS的根目录 hadoop fs -put a.txt /2.通过页面查看…...

go cobra初试
cobra开源地址 https://github.com/spf13/cobra cobra是什么 Cobra is a library for creating powerful modern CLI applications. Cobra is used in many Go projects such as Kubernetes, Hugo, and GitHub CLI to name a few. This list contains a more extensive lis…...
【react全家桶】 事件处理
文章目录03 【事件处理】1.React事件2.类式组件绑定事件3.向事件处理程序传递参数4.收集表单数据5.受控和非受控组件5.函数的柯里化03 【事件处理】 React的事件是通过onXxx属性指定事件处理函数 React 使用的是自定义事件,而不是原生的 DOM 事件 React 的事件是通过…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...