当前位置: 首页 > 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…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

DAY 26 函数专题1

函数定义与参数知识点回顾&#xff1a;1. 函数的定义2. 变量作用域&#xff1a;局部变量和全局变量3. 函数的参数类型&#xff1a;位置参数、默认参数、不定参数4. 传递参数的手段&#xff1a;关键词参数5 题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一…...

Qt的学习(一)

1.什么是Qt Qt特指用来进行桌面应用开发&#xff08;电脑上写的程序&#xff09;涉及到的一套技术Qt无法开发网页前端&#xff0c;也不能开发移动应用。 客户端开发的重要任务&#xff1a;编写和用户交互的界面。一般来说和用户交互的界面&#xff0c;有两种典型风格&…...