当前位置: 首页 > news >正文

【c++进阶(二)】STL之string类的模拟实现

💓博主CSDN主页:Am心若依旧💓

⏩专栏分类c++从入门到精通⏪
🚚代码仓库:青酒余成🚚

🌹关注我🫵带你学习更多c++
  🔝🔝

 

 1.前言

本章重点

本章主要介绍一些关键接口的模拟实现,例如:构造函数,拷贝构造函数,析构函数,赋值重载函数,插入删除函数等等。

 2.默认成员函数

在实现默认成员函数之前,先确定成员变量

namespace wzz
{class string{private:char* _str;//用一个字符数组来模拟stringsize_t _size;//数组中实际有效的个数size_t _capacity;//数组中容量的大小static const size_t npos;//静态成员变量来表示最大值};const string::size_t npos=-1;//静态成员变量在类内声明,在类外定义
}

1.默认构造函数--当没有传值的时候,就用一个缺省值来代替--这个缺省值用空字符串代替

string (const char* str="")
{_str=new char[_capacity+1];_size=strlen(str);_capacity=_size;//初始时,容量就让其等于有效元素的个数strcpy(_str,str);
}

补充迭代器构造

template <class Inputeiterrator>
string(Inputeiterator begin,Inputeriterator end)
{while(begin!=end){push_back(*begin);begin++;        }
}

2.拷贝构造函数--用一个char*来初始化另一个char*

在这里会详细介绍深浅拷贝--后续就不在重点介绍了

浅拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间。其中一个对象的改动会对另一个对象造成影响。
深拷贝:深拷贝是指源对象与拷贝对象互相独立。其中任何一个对象的改动不会对另外一个对象造成影响。

很明显,深拷贝比浅拷贝更加安全,不会有同一块空间析构两次的问题。 这里拷贝构造就介绍两种写法

法一:开辟一块空间,然后把数据拷贝过来--如下图

代码:

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

法二:利用swap函数

法二与法一的思想不同:先根据源字符串的C字符串调用构造函数构造一个tmp对象,然后再将tmp对象与拷贝对象的数据交换即可。拷贝对象的_str与源对象的_str指向的也不是同一块空间,是互相独立的。

代码如下:


string(const string& s):_str(nullptr), _size(0), _capacity(0)
{string tmp(s.begin(),s.end()); //调用构造函数(这里用的是迭代器构造),构造出一个C字符串为s._str的对象swap(tmp); //交换这两个对象
}

swap函数的模拟实现

只需要把每个成员变量进行交换即可

//交换两个对象的数据
void swap(string& s)
{//调用库里的swapstd::swap(_str, s._str); //交换两个对象的C字符串std::swap(_size, s._size); //交换两个对象的大小std::swap(_capacity, s._capacity); //交换两个对象的容量
}

这里推荐使用方法二来模拟实现构造函数

3.赋值重载函数

这里也涉及到深浅拷贝的问题--因此提供两种深拷贝的写法

法一:把原来的空间直接释放,然后开辟新空间,然后再赋值

//法一
string& operator=(const string& s)
{if (this != &s) //防止自己给自己赋值{delete[] _str; //将原来_str指向的空间释放_str = new char[s._capacity + 1]; //重新申请一块空间strcpy(_str, s._str);    //将s._str拷贝一份到_str_size = s._size;         //_size赋值_capacity = s._capacity; //_capacity赋值}return *this; //返回左值(支持连续赋值)
}

法二:用交换函数

//方法二
string& operator=(const string& s)
{if (this != &s) //防止自己给自己赋值{string tmp(s); //用s拷贝构造出对象tmpswap(tmp); //交换这两个对象}return *this; //返回左值(支持连续赋值)
}

注意:

用传值返回还是传引用返回,要看你返回的这个值除了作用域还在不在,如果在的话就可以使用传值返回,如果不在的话就一定要使用传引用返回,否则就会报错。

4.析构函数

string类的析构函数需要我们进行编写,因为每个string对象中的成员_str都指向堆区的一块空间,当对象销毁时堆区对应的空间并不会自动销毁,为了避免内存泄漏,我们需要使用delete手动释放堆区的空间。

//析构函数
~string()
{delete[] _str;  //释放_str指向的空间_str = nullptr; //及时置空,防止非法访问_size = 0;      //大小置0_capacity = 0;  //容量置0
}

 3.迭代器相关函数

 string类中的迭代器实际上就是字符指针,只是给字符指针起了一个别名叫iterator而已。

typedef char* iterator;
typedef const char* const_iterator;
 

 注意:不是所有的迭代器都是指针,比如在list中迭代器就是节点

begin函数---返回字符的第一个地址

iterator begin()
{return _str; //返回字符串中第一个字符的地址
}
const_iterator begin()const
{return _str; //返回字符串中第一个字符的const地址
}

end函数---返回字符的最后一个地址(即\0的地址)

iterator end()
{return _str + _size; //返回字符串中最后一个字符的后一个字符的地址
}
const_iterator end()const
{return _str + _size; //返回字符串中最后一个字符的后一个字符的const地址
}

4.容量大小相关的函数

size和capacity

因为string类的成员变量是私有的,我们并不能直接对其进行访问,所以string类设置了size和capacity这两个成员函数,用于获取string对象的大小和容量
size函数用于获取字符串当前的有效长度(不包括’\0’)。

//大小
size_t size()const
{return _size; //返回字符串当前的有效长度
}

capacity用于获取当前的容量

//容量
size_t capacity()const
{return _capacity; //返回字符串当前的容量
}

reverse和resize

reverse--只有当大于当前容量时才会进行扩容,其他不关心

//改变容量,大小不变
void reserve(size_t n)
{if (n > _capacity) //当n大于对象当前容量时才需执行操作{char* tmp = new char[n + 1]; //多开一个空间用于存放'\0'strncpy(tmp, _str, _size + 1); //将对象原本的C字符串拷贝过来(包括'\0')delete[] _str; //释放对象原本的空间_str = tmp; //将新开辟的空间交给_str_capacity = n; //容量跟着改变}
}

注意:代码中使用strncpy进行拷贝对象C字符串而不是strcpy,是为了防止对象的C字符串中含有有效字符’\0’而无法拷贝(strcpy拷贝到第一个’\0’就结束拷贝了)。

reseize

 1、当n大于当前的size时,将size扩大到n,扩大的字符为ch,若ch未给出,则默认为’\0’。
 2、当n小于当前的size时,将size缩小到n。

//改变大小
void resize(size_t n, char ch = '\0')
{if (n <= _size) //n小于当前size{_size = n; //将size调整为n_str[_size] = '\0'; //在size个字符后放上'\0'}else //n大于当前的size{if (n > _capacity) //判断是否需要扩容{reserve(n); //扩容}for (size_t i = _size; i < n; i++) //将size扩大到n,扩大的字符为ch{_str[i] = ch;}_size = n; //size更新_str[_size] = '\0'; //字符串后面放上'\0',这里如果不放\0后续就会产生错误}
}

empty--判断是否还有元素

//判空
bool empty()
{return _size== 0;
}

5.与插入删除有关的函数

insert函数的作用是在字符串的任意位置插入字符或是字符串。
insert函数用于插入字符时,首先需要判断pos的合法性,若不合法则无法进行操作,紧接着还需判断当前对象能否容纳插入字符后的字符串,若不能则还需调用reserve函数进行扩容。插入字符的过程也是比较简单的,先将pos位置及其后面的字符统一向后挪动一位,给待插入的字符留出位置,然后将字符插入字符串即可。

//在pos位置插入字符
string& insert(size_t pos, char ch)
{assert(pos <= _size); //检测下标的合法性if (_size == _capacity) //判断是否需要增容{reserve(_capacity == 0 ? 4 : _capacity * 2); //将容量扩大为原来的两倍}char* end = _str + _size;//将pos位置及其之后的字符向后挪动一位while (end >= _str + pos){*(end + 1) = *(end);end--;}_str[pos] = ch; //pos位置放上指定字符_size++; //size更新return *this;
}

如果要用于插入字符串也是和上述插入一个字符同理

//在pos位置插入字符串
string& insert(size_t pos, const char* str)
{assert(pos <= _size); //检测下标的合法性size_t len = strlen(str); //计算需要插入的字符串的长度(不含'\0')if (len + _size > _capacity) //判断是否需要增容{reserve(len + _size); //增容}char* end = _str + _size;//将pos位置及其之后的字符向后挪动len位while (end >= _str + pos){*(end + len) = *(end);end--;}memcpy(_str + pos, str, len); //pos位置开始放上指定字符串_size += len; //size更新return *this;
}

push_back和push_front

void push_back(char ch)
{insert(_size(),ch);
}void push_front(char ch)
{insert(0,ch);
}

erase函数

erase函数的作用是删除字符串任意位置开始的n个字符。删除字符前也需要判断pos的合法性,进行删除操作的时候分两种情况:
1、pos位置及其之后的有效字符都需要被删除。
这时我们只需在pos位置放上’\0’,然后将对象的size更新即可。

2.删除pos位置的一部分len长度的字符

//删除pos位置开始的len个字符
string& erase(size_t pos, size_t len = npos)
{assert(pos < _size); //检测下标的合法性--下标是0-_size-1,所以删除的位置不能超过_sizesize_t n = _size - pos; //pos位置及其后面的有效字符总数if (len >= n) //说明pos位置及其后面的字符都被删除{_size = pos; //size更新_str[_size] = '\0'; //字符串后面放上'\0'}else //说明pos位置及其后方的有效字符需要保留一部分{strcpy(_str + pos, _str + pos + len); //用需要保留的有效字符覆盖需要删除的有效字符_size -= len; //size更新}return *this;
}

pop_back和pop_front函数

void pop_back()
{erase(_size,1);
}void pop_front()
{erase(0,1);
}

append函数

主要含义就是在尾部追加字符串

//尾插字符串
void append(const char* str)
{insert(_size, str); //在字符串末尾插入字符串str
}

6.访问相关函数

operator[]---实际是一个函数重载

//[]运算符重载(可读可写)---由于是char*的结构,支持下标随机访问
char& operator[](size_t i)
{assert(i < _size); //检测下标的合法性return _str[i]; //返回对应字符
}//只能读
const char& operator[](size_t i)
{assert(i < _size); //检测下标的合法性return _str[i]; //返回对应字符
}

find函数

 找到了就返回下标,没找到就返回npos,即-1

//正向查找第一个匹配的字符
size_t find(char ch, size_t pos = 0)
{assert(pos < _size); //检测下标的合法性for (size_t i = pos; i < _size; i++) //从pos位置开始向后寻找目标字符{if (_str[i] == ch){return i; //找到目标字符,返回其下标}}return npos; //没有找到目标字符,返回npos
}

7.总结

string类的大部分重要接口都已经完全的实现了,希望这部分内容能够重点掌握--因为在面试的过程中很有可能考官让你手撕一个string类

                                                下期预告:vector 

相关文章:

【c++进阶(二)】STL之string类的模拟实现

&#x1f493;博主CSDN主页:Am心若依旧&#x1f493; ⏩专栏分类c从入门到精通⏪ &#x1f69a;代码仓库:青酒余成&#x1f69a; &#x1f339;关注我&#x1faf5;带你学习更多c   &#x1f51d;&#x1f51d; 1.前言 本章重点 本章主要介绍一些关键接口的模拟实现&#xff…...

PHPStudy(xp 小皮)V8.1.1 通过cmd进入MySQL命令行模式

PHPStudy是一个PHP开发环境集成包&#xff0c;可用在本地电脑或者服务器上&#xff0c;该程序包集成最新的PHP/MySql/Apache/Nginx/Redis/FTP/Composer&#xff0c;一次性安装&#xff0c;无须配置即可使用。MySQL MySQL是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL A…...

php反序列化初步了解

一、定义 序列化&#xff08;串行化&#xff09;&#xff1a;将变量转换为可保存或传输的字符串的过程&#xff08;通常是字节流、JSON、XML格式&#xff09; 反序列比&#xff08;反串行化&#xff09;&#xff1a;把这个字符串再转化成原始数据结构或对象&#xff08;原来的…...

Windows系统电脑本地部署AI音乐创作工具并实现无公网IP远程使用

文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统电脑上快速本地部署一个文字生成音乐的AI创作工具MusicGPT&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问使用。 MusicG…...

玩转Linux进度条

准备工作&#xff1a; 一.关于缓冲区 首先&#xff0c;咱们先来一段有意思的代码&#xff1a; #include<stdio.h> #include<unistd.h> int main() {printf("you can see me");sleep(5);} 你可以在你的本地运行一下&#xff0c;这里我告诉大家运行结果…...

真国色码上赞,科技流量双剑合璧,商家获客新纪元开启

在数字化浪潮汹涌的今天,真国色研发团队依托红玉房网络科技公司的雄厚实力,凭借科技领先的核心竞争力,推出了创新性的商家曝光引流工具——码上赞。这款工具借助微信支付与视频号已有功能,为实体商家提供了一种全新的引流获客方式,实现了科技与商业的完美融合。 科技领先,流量黑…...

C++:特殊类设计和四种类型转换

一、特殊类设计 1.1 不能被拷贝的类 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98&#xff1a; 1、将拷贝构造函数与赋值运算符重载只…...

(南京观海微电子)——屏幕材质及优缺点对比

LED/LCD LCD&#xff08;Liquid Crystal Ddisplay&#xff09;即“液晶显示器”&#xff0c;由两块偏光镜、两块薄膜晶体管以及彩色滤光片、光源&#xff08;荧光灯&#xff09;、显示面板组成的成像元器件。 LED&#xff08;Light Emitting Diode&#xff09;即“发光二极管…...

uniapp uni.showModal 出现点击没有反应

uni.showModal 里面有好些参数 点击后不弹出 是因为 出现了 null 或者undifind 字符 特别是content 里面 title: 提示, cancelColor: #000000, editable: true,//是否显示输入框 content: item.text?item.te…...

Vue3-VueRouter

客户端 vs. 服务端路由​ 服务端路由指的是服务器根据用户访问的 URL 路径返回不同的响应结果。当我们在一个传统的服务端渲染的 web 应用中点击一个链接时&#xff0c;浏览器会从服务端获得全新的 HTML&#xff0c;然后重新加载整个页面。 然而&#xff0c;在单页面应用中&a…...

【图像处理与机器视觉】频率域滤波

知识铺垫 复数 CRjI 可以看作复平面上的点&#xff0c;则该复数的坐标为&#xff08;R&#xff0c;I&#xff09; 欧拉公式 e j θ c o s θ j s i n θ e^{j\theta} cos \theta j sin \theta ejθcosθjsinθ 极坐标系中复数可以表示为&#xff1a; C ∣ C ∣ ( c o s…...

python第五次作业

1.请实现一个装饰器&#xff0c;每次调用函数时&#xff0c;将函数名字以及调用此函数的时间点写入文件中 # 导入datetime模块&#xff0c;用于获取当前时间并格式化输出 import datetime# 定义一个装饰器工厂函数log_funcName_time&#xff0c;它接受一个参数time def log_fu…...

JS面向对象编程

目录 实例对象与new命令this关键字对象的原型和继承Object对象的相关方法浅拷贝和深拷贝严格模式实例对象与new命令 构造函数 构造函数的特点有两个: 函数体内部使用了this关键字,代表了所要生成的对象实例。生成对象的时候,必须使用new命令。var Vehicle...

kotlin1.8.10问题导致gson报错TypeToken type argument must not contain a type variable

书接上回&#xff0c;https://blog.csdn.net/jzlhll123/article/details/139302991。 之前我发现gson报错后&#xff1a; gson在2.11.0给我的kotlin项目代码报错了。 IllegalArgumentException: TypeToken type argument must not contain a type variable 上次解释原因是因为&…...

数据库漫谈-国产数据库

国产数据库突然大量出现&#xff0c;下表列出&#xff08;按首字母排序 &#xff09; AISWare AntDB&#xff0c;亚信科技。 AliSQL&#xff0c;阿里云。 Analyticdb&#xff0c;阿里云。 ArkDB&#xff0c;北京极数云舟科技有限公司。 CynosDB&#xff0c;腾讯云 DM&…...

小白跟做江科大32单片机之光敏传感器控制蜂鸣器

代码部分 1.思路 通过光敏电阻&#xff0c;控制蜂鸣器的发声 2.butter.h代码 #ifndef _BUTTER__H #define _BUTTER__H void butter_Init(void); void butter_on(void); void butter_off(void); #endif 3.butter.c代码 #include "stm32f10x.h" void butter…...

使用 Django Channels 构建实时聊天应用(包含用户认证和消息持久化)

文章目录 准备工作创建 Django 项目创建应用程序配置项目编写 Consumer编写路由创建 URL 路由运行应用用户认证消息持久化显示历史消息结论 Django Channels 是 Django 的一个扩展&#xff0c;允许在 Web 应用中添加实时功能&#xff0c;例如 Websockets、HTTP2 和其他协议。本…...

【Elasticsearch】es基础入门-03.RestClient操作文档

RestClient操作文档 示例&#xff1a; 一.初始化JavaRestClient &#xff08;一&#xff09;引入es的RestHighLevelClient依赖 <!--elasticsearch--> <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest…...

LeetCode - 二分查找(Binary Search)算法集合(Python)[左右边界|旋转数组|双列表]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139419653 二分查找&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。其基本原理是将待搜索的区间分成两半&am…...

android睡眠分期图

一、效果图 做医疗类项目&#xff0c;经常会遇到做各种图表&#xff0c;本文做的睡眠分期图。 二、代码 引入用到的库 api joda-time:joda-time:2.10.1 调用代码 /*** 睡眠* 分期*/private SleepChartAdapter mAdapter;private SleepChartAttrs mAttrs;private List<SleepI…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...