【C++进阶二】string的模拟实现

【C++进阶二】string的模拟实现

  • 1.构造函数和C_str
    • C_str:
  • 2.operator[]
  • 3.拷贝构造
    • 3.1浅拷贝
    • 3.2深拷贝
  • 4.赋值
  • 5.迭代器
  • 6.比较ascll码值的大小
  • 7.reverse扩容
  • 8.push_back尾插和append尾插
  • 9.+=
  • 10.insert
    • 10.1在pos位置前插入字符ch
    • 10.2在pos位置前插入字符串str
  • 11.resize
  • 12.erase
    • 12.1从pos位置开始删除len个数据
    • 12.2当pos+len<总长度时,使用strcpy函数拷贝,从而覆盖删除要被删除的字符
    • 12.3当pos+len大于总长度或者len等于npos时,剩余长度全部删除
  • 13.流插入<<
  • 14.流提取 >>

string底层是一个字符数组,为了跟库里的string区别,我们定义了一个命名空间将string类包含咋内

1.构造函数和C_str

错误示范:

在这里插入图片描述
使用new开辟空间可以再次改进构造函数:
在这里插入图片描述
最终优化为全缺省的构造函数:
在这里插入图片描述

不可以将缺省值设置成nullptr,strlen(str)对于str指针解引用,遇到’\0’终止,解引用NULL会报错。将缺省值设置成一个空字符串,结尾默认为’\0’

C_str:


const char* C_str()//返回const char*类型的指针 
{return  _str;
}

返回const char*类型的指针相当于返回字符串

2.operator[]

由于可能存在 string与const string类型,所以设置成两个函数构成函数重载

char& operator[](size_t pos)//operator[]
{return _str[pos];
}
char& operator[](size_t pos)const//operator[]的函数重载
{return _str[pos];
}

3.拷贝构造

3.1浅拷贝

拷贝构造函数如果不写编译器会自动生成,对于内置类型完成值拷贝或者浅拷贝,若使用编译器自动生成的拷贝构造就会报错

string s2("hello world");
string s3(s2);

在这里插入图片描述

3.2深拷贝

string(const string& s)//拷贝构造:_size(s._size),_capaicty(s._capaicty)
{_str = new char[_capaicty + 1];//开辟一块空间strcpy(_str, s._str);//将s2拷贝给s1
}

创建一块同样大小的空间,并将原来的数据拷贝下来,这样就是s2与s3指向各自的空间,一个被修改也不会影响另一个
在这里插入图片描述

4.赋值

赋值运算符也是默认成员函数,如果不写会进行浅拷贝/值拷贝
在这里插入图片描述

  1. 第一种两者空间大小相同,进行值拷贝
  2. 第二种s1的空间远大于s2的空间,进行值拷贝会浪费空间,所以系统会按照第三种做法执行
  3. 第三种,s1的空间太小,需要new开辟一块空间,将旧空间销毁,将s2拷贝到新开辟的空间

编译器默认不会这样处理,它会直接将旧空间释放,再去开新空间,并将值拷贝过来

string& operator=(const string& s)//赋值 s1=s3
{if(this != &s)//排除赋值本身的情况{char* tmp = new char[s._capaicty + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capaicty = s._capaicty;}return *this;
}

若释放旧空间,如果new失败了,则破坏原有空间,所以使用一个临时变量tmp接收开辟的空间,如果new成功将tmp传给_str,若new失败也不会破坏s1空间

5.迭代器

使用typedef 分别将用iterator代替 char*,const_iterator代替 const char*

typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}
   
iterator begin()const //指针指向的内容不能被修改
{return _str;
}
iterator end()const
{return _str + _size;
}

在这里插入图片描述

6.比较ascll码值的大小

通过C语言函数strcmp,比较字符串从头开始字符的ASCII值,再通过复用来实现剩下的
如果不小心在复用时将const修饰的传给非const成员就会报错,所以括号外面加上const,修饰this指针

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

7.reverse扩容

void reserve(size_t n)//开辟空间
{if(n > _capacity)//防止缩容的问题{char* tmp = new char[n + 1];//多开一个'\0'strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;//计算有效}
}

在这里插入图片描述
为了防止new失败,所以使用临时变量tmp指向new出来的空间,若new成功,释放旧空间,并将_str指向新空间

8.push_back尾插和append尾插

通过reserve进行类似扩容的操作,再将ch赋值给当前最后一个字符

void push_back(char ch)//尾插字符
{if(_size + 1 > _capaicty){reserve(2 * _capaicty);//开辟2倍空间}_str[_size] = ch;_size++;
}
void append(const char* str)//尾插 字符串
{ int len = strlen(str);if(_size + len > _capaicty){reserve(_size + len);}strcpy(_str + _size, str);//在原来的字符串后拷贝字符串_size += len;
}

9.+=

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

10.insert

10.1在pos位置前插入字符ch

void insert(size_t pos, char ch)//在pos位置前插入字符ch
{if(_size + 1 > _capacity)//扩容{reserve(2 * _capacity);}size_t end = _size + 1;//'\0'的下一个位置while(end > pos){//把前面传给后面_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;
}

由于pos与end都是size_t类型,没有负数,所以当while循环条件设置为end>=pos时,如果pos=0,end–-,end变为负数,计算的就是其补码了,所以while(end>=pos)一直成立,无法结束循环

在这里插入图片描述
把前面的字符传给后面的字符,当end下标为1时,end-1的下标为0,循环结束

10.2在pos位置前插入字符串str

void insert(size_t pos, const char* str)//在pos位置前插入字符串str
{int len = strlen(str);if(_size + len > _capacity)//扩容{reserve(_size + len);}size_t end = _size + len;while(end > pos+len-1){//把前面传给后面_str[end] = _str[end-len];end--;}strncpy(_str + pos, str, len);//拷贝len个字节,不包含'\0'_size += len;
}

在这里插入图片描述

为了保证最后一次循环时,下标end-len在下标为0的位置上,所以取end为pos+len,如果end>pos+len-1不成立时终止循环,最后一次end取值即为pos+len
使用strncpy函数,将不包含’\0’的str拷贝到_str+pos下标位置

11.resize

分为三种情况

  1. n<size 删除数据
  2. size<n<capacity 剩余空间初始化
  3. n>capacity 扩容+初始化
void resize(size_t n,char ch)//开辟空间+初始化
{if(n <= _size)//删除数据保留前n个{_size = n;_str[n] = '\0';}else//n>_size{if(n >_capacity)//扩容{reserve(n);}int i = _size;while(i < n)//剩余空间初始化为ch{_str[i] = ch;i++;}_size = n;_str[_size] = '\0';}
}

12.erase

12.1从pos位置开始删除len个数据

static const size_t npos = -1;
string& erase(size_t pos = 0, size_t len = npos)//从pos位置开始删除len个数据
{if(len==npos||pos+len>=_size){//全部删除_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str +pos+len);//包含'\0'_size -= len;}return *this;
}

12.2当pos+len<总长度时,使用strcpy函数拷贝,从而覆盖删除要被删除的字符

12.3当pos+len大于总长度或者len等于npos时,剩余长度全部删除

13.流插入<<

使用友元函数是为了在类外面调用类的私有的成员变量,若不需要调用则不用友元函数

ostream& operator<<(ostream& out, const string&s)
{int i = 0;for(i = 0; i < s.size(); i++){out << s[i] << " ";}return out;
}

实现流插入不可以调用C_str(),因为C_str()返回的是一个字符串,遇见’\0’就会结束,但若打印结果有好几个’\0’,则遇见第一个就会结束,不符合预期

14.流提取 >>

输入多个值,C++规定空格换行是值与值之间的区分
错误示范:

istream& operator>>(istream& in,  string& s)//>>
{char ch;in >> ch;while(ch != ' ' && ch != '\n'){s += ch;in >> ch;}return in;
}
  1. 上述代码在循环中无法找到空格/换行,导致循环无法停止
  2. 输入的数据存放在缓冲区中,使用循环在缓冲区中提取数据,但是空格/换行不在缓冲区中,因为认为它是多个值之间的间隔
  3. 使用get就不会认为空格/换行是多个值之间的间隔,若遇见空格/换行就会存放缓冲区中等待提取
istream& operator>>(istream& in,  string& s)//>>
{s.clear();char ch = in.get();char buf[128];size_t index = 0;while(ch != ' ' && ch != '\n'){buf[index++] = ch;if(index == 127)//为了防止频繁扩容{buf[127] = '\0';s += buf;index = 0;}ch = in.get();}if(index != 0){buf[index] = '\0';s += buf;}return in;
}

当需要输入的string对象中有值存在时,需要先使用clear清空,再输入新的数据
为了避免频繁扩容,使用一个128的字符数组接收,若输入的数据比128小,跳出循环将数组中的数据传给string类s,若输入的数据比128大,则将字符数组整体传给string类s,再正常扩容

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

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

相关文章

wokwi arduino mega 2560 - 点亮LED案例

截图&#xff1a; 点亮LED案例仿真截图 代码&#xff1a; unsigned long t[20]; // 定义一个数组t&#xff0c;用于存储20个LED的上次状态切换时间&#xff08;单位&#xff1a;毫秒&#xff09;void setup() {pinMode(13, OUTPUT); // 将引脚13设置为输出模式&#xff08;此…

vue3项目使用 python +flask 打包成桌面应用

server.py import os import sys from flask import Flask, send_from_directory# 获取静态文件路径 if getattr(sys, "frozen", False):# 如果是打包后的可执行文件base_dir sys._MEIPASS else:# 如果是开发环境base_dir os.path.dirname(os.path.abspath(__file…

后端学习day1-Spring(八股)--还剩9个没看

一、Spring 1.请你说说Spring的核心是什么 参考答案 Spring框架包含众多模块&#xff0c;如Core、Testing、Data Access、Web Servlet等&#xff0c;其中Core是整个Spring框架的核心模块。Core模块提供了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能&#xff0c;…

LeetCode 第34、35题

LeetCode 第34题&#xff1a;在排序数组中查找元素的第一个和最后一个位置 题目描述 给你一个按照非递减顺序排列的整数数组nums&#xff0c;和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target&#xff0c;返回[-1,1]。你必须…

告别分库分表,时序数据库 TDengine 解锁燃气监控新可能

达成效果&#xff1a; 从 MySQL 迁移至 TDengine 后&#xff0c;设备数据自动分片&#xff0c;运维更简单。 列式存储可减少 50% 的存储占用&#xff0c;单服务器即可支撑全量业务。 毫秒级漏气报警响应时间控制在 500ms 以内&#xff0c;提升应急管理效率。 新架构支持未来…

第十四届蓝桥杯真题

一.LED 先配置LED的八个引脚为GPIO_OutPut,锁存器PD2也是,然后都设置为起始高电平,生成代码时还要去解决引脚冲突问题 二.按键 按键配置,由原理图按键所对引脚要GPIO_Input 生成代码,在文件夹中添加code文件夹,code中添加fun.c、fun.h、headfile.h文件,去资源包中把lc…

《基于机器学习发电数据电量预测》开题报告

个人主页&#xff1a;大数据蟒行探索者 目录 一、选题背景、研究意义及文献综述 &#xff08;一&#xff09;选题背景 &#xff08;二&#xff09;选题意义 &#xff08;三&#xff09;文献综述 1. 国内外研究现状 2. 未来方向展望 二、研究的基本内容&#xff0c;拟解…

UWP程序用多页面实现应用实例多开

Windows 10 IoT ARM64平台下&#xff0c;UWP应用和MFC程序不一样&#xff0c;同时只能打开一个应用实例。以串口程序为例&#xff0c;如果用户希望同时打开多个应用实例&#xff0c;一个应用实例打开串口1&#xff0c;一个应用实例打开串口2&#xff0c;那么我们可以加载多个页…

Springboot整合Netty简单实现1对1聊天(vx小程序服务端)

本文功能实现较为简陋&#xff0c;demo内容仅供参考&#xff0c;有不足之处还请指正。 背景 一个小项目&#xff0c;用于微信小程序的服务端&#xff0c;需要实现小程序端可以和他人1对1聊天 实现功能 Websocket、心跳检测、消息持久化、离线消息存储 Netty配置类 /*** au…

GitLab 中文版17.10正式发布,27项重点功能解读【二】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

好消息!软航文档控件(NTKO WebOffice)在Chrome 133版本上提示扩展已停用的解决方案

软航文档控件现有版本依赖Manifest V2扩展技术支持才能正常运行&#xff0c;然而这个扩展技术到2025年6月在Chrome高版本上就彻底不支持了&#xff0c;现在Chrome 133开始的版本已经开始弹出警告&#xff0c;必须手工开启扩展支持才能正常运行。那么如何解决这个技术难题呢&…

字典树与01trie

字典树简介 当我们通过字典查一个字或单词的时候&#xff0c;我们会通过前缀或关键字的来快速定位一个字的位置&#xff0c;进行快速查找。 字典树就是类似字典中索引表的一种数据结构&#xff0c;能够帮助我们快速定位一个字符串的位置。 字典树是一种存储字符串的数据结构…

二十五、实战开发 uni-app x 项目(仿京东)- 前后端轮播图

定义了一个名为 Swiper 的Java类,用于表示一个轮播图实体。它使用了 Jakarta Persistence API (JPA) 来映射数据库表,并使用了 Lombok 库来简化代码。以下是对代码的详细讲解: 1. 包声明 package com.jd.jdmall.model; 这行代码声明了该类所在的包路径为 com.jd.jdmall.mode…

游戏摇杆开发:利用 Windows API 实现摇杆输入捕获

在现代游戏开发中&#xff0c;游戏摇杆&#xff08;Joystick&#xff09;作为一种重要的输入设备&#xff0c;能够为玩家提供更加沉浸式的游戏体验。Windows 操作系统提供了一系列 API 函数&#xff0c;允许开发者轻松地捕获和处理游戏摇杆的输入。本文将介绍如何使用 Windows …

Ceph集群2025(Squid版)快速对接K8S cephFS文件存储

ceph的块存储太简单了。所以不做演示 查看集群 创建一个 CephFS 文件系统 # ceph fs volume create cephfs01 需要创建一个子卷# ceph fs subvolume create cephfs01 my-subvol -----------------#以下全部自动创建好 # ceph fs ls name: cephfs01, metadata pool: c…

Python中数据结构元组详解

在Python中&#xff0c;元组&#xff08;Tuple&#xff09;是一种不可变的序列类型&#xff0c;常用于存储一组有序的数据。与列表&#xff08;List&#xff09;不同&#xff0c;元组一旦创建&#xff0c;其内容无法修改。本文将详细介绍元组的基本操作、常见运算、内置函数以及…

游戏引擎学习第183天

回顾和今天的计划 我对接下来的进展感到非常兴奋。虽然我们可能会遇到一些问题&#xff0c;但昨天我们差不多完成了将所有内容迁移到新的日志系统的工作&#xff0c;我们正在把一些内容整合进来&#xff0c;甚至是之前通过不同方式记录时间戳的旧平台层部分&#xff0c;现在也…

Spring 如何处理循环依赖

在 Spring 框架里&#xff0c;循环依赖指的是多个 Bean 之间相互依赖&#xff0c;从而形成一个闭环。例如&#xff0c;Bean A 依赖 Bean B&#xff0c;而 Bean B 又依赖 Bean A。Spring 主要通过三级缓存机制来处理循环依赖&#xff0c;下面详细介绍相关内容。 1. 三级缓存的定…

Android开发layer-list

Android开发layer-list 它的用处可以在drawable上进行多图拼接&#xff0c;比如启动页&#xff0c;不想图片被拉伸就这么做。还有做某些线突出来。 示例代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android&q…

手机测试,工作中学习

要学习各种机型的截图方式、开发模式在哪。 荣耀机型&#xff1a;截图&#xff1a;关节快速敲两下。开发者模式在“系统和更新”里。 1.出现缺陷&#xff0c;需要获取日志。 学习adb生成日志&#xff1a;当测试中出现缺陷的&#xff0c;使用adb logcat -d > d:/log.txt …