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

C++之“string”类的模拟实现

                  

                       

                                         🌹个人主页🌹:喜欢草莓熊的bear

                                                🌹专栏🌹:C++入门

前言

hello ,大家又来跟着bear学习了。一起奔向更好的自己,上篇博客已经讲清楚了string的一些功能的使用。我们就实现一些主要的功能。

一、实现string的思路

头文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;

我们先写一个大框架,我的是放在了rose这个命名空间里面。定义完了成员变量,还给了缺省值。

namespace rose
{class string
{
public:private:
char* _str=nullptr;
size_t _size=0;
size_t _capacity=0;
}}

 因为成员变量不是自定义类型,所以我们需要自己写构造和析构函数。

构造函数

如果没有传参数,我们就给一个”“,有的话我们就拷完成深拷贝,浅拷贝会导致内存问题。再原有基础上多开一个空间是方便储存\0。

string(const char* str="")//构造函数
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//储存\0strcpy(_str, str);//参数1是destination(目标)  参数2是source(源)
}//还是一样深拷贝
string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

析构函数 

我们释放我们之前申请的空间,如果申请了就释放,没有就不释放空间。

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

size和capacity

很简单不用多说

size_t size()const//元素个数
{return _size;
}size_t capacity()const//容量
{return _capacity;
}

 观察发现string的这些接口都是使用迭代器的,所以我们使用typedef 重定义一下 char* 和 const char*。

typedef char* iterator;//重定义
typedef const char* const_iterator;

 begin和end

begin返回最开始的位置也将是首元素,end是最后一个元素。std库里面提供了两种我们也实现两种。

iterator begin()//因为成员函数,所以第一个参数不用写默认指向this指针
{return _str;
}const_iterator begin()const
{return _str;
}iterator end()
{return _str + _size;
}const_iterator end()const
{return _str + _size;
}

 拷贝构造函数

浅拷贝会导致内存释放问题,所以还是采用深拷贝。

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

 clear和c_str

clear是清除动态数组里面的字符,只剩\0,c_str是返回c语言风格的。

 这两个也很简单实现

void clear()//清楚字符串
{_str[0] = '\0';_size = 0;
}const char* c_str() const//就是单纯返回C语言的风格,比如string类型直接使用strcmp不安全。
{return _str;
}

 重载operator = 和 operator []

先看库里面string的”=“ 与 ”[]“。

重载[]也就是返回下标元素,注意的是要传合法下标(注意一下越界)

char& operator[](size_t pos)//【】
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}

 重载”=“分为两种一个是传统写法,还有一个现代写法。

传统写法就是什么都考自己来完成,我们是举例把s2赋值给s1。

先把之前原来数组里面的空间给释放,再重新申请s2的空间+1为了储存\0。接着把s2的成员变量拷贝给s1的成员变量.空间大小申请好了我们就把s2的字符复制给s1.这里调用strcpy。再判断一下s1和s2是否相等。

传统赋值
s1 = s2
string& operator=(const string& s)
{if (this != &s)//this指针是地址所以需要取s的地址来比较{delete[]_str;_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);}return *this;
}

 现代写法,大部分考编译器来做。

借助拷贝构造完成中间对象的创建,并且把中间对象和this指针的对象进行交换(指向的空间,数据个数,容量)又因为tmp出来作用域会销毁所以,还顺便把this之前的空间也给销毁了。

void swap(string& str)
{std::swap(_str, str._str);//std::swap是库里面的std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}//借助拷贝构造完成中间对象的创建,并且把中间对象和this指针的对象进行交换(指向的空间,数据个数,容量)
//又因为tmp出来作用域会销毁所以,还顺便把this之前的空间也给销毁了。
string& operator=(const string& s)//现代赋值
{if (this != &s)//this指针是地址所以需要取s的地址来比较{string tmp(s);swap(tmp);}return *this;
}//s1 = s2
string& operator=(string tmp)//直接使用临时对象来接收s2的值//因为是传值传参使用会调用拷贝构造//这里判断他们相等意义不大,可判 可不判
{swap(tmp);return *this;
}

 我们这些写的都是内联函数,频繁调用且代码不长的。

reserve、push_back和append

reserve:目的扩容,首先判断需不需要扩容。我们采用深拷贝,把原数组里面的内容cpy到临时数组tmp里面。

push_back:目的插入字符,分为容量有没有满,没满就直接尾插,满了扩容后继续尾插。一般扩容是按照两倍来的。

append:目的是在末尾加入字符串,和push_back一样是否需要扩容,因为是字符串所以我们采用strcpy更快,但是初始位置是_str+size。

void string::reserve(size_t n = 0)//扩容函数
{if (n > _capacity){char* tmp = new char[n + 1];//多一个用来装\0strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}
}void string::push_back(char ch)
{if (_capacity = _size){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';
}void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){// 大于2倍,需要多少开多少,小于2倍按2倍扩reserve(_size+len > 2 * _capacity ? _size + len :2*_capacity);}strcpy(_str + _size, str);//因为append是要实现在字符串后面加字符串,这里是_str(数组首元素)+_size,正好拷贝的时候覆盖率旧的\0_size += len;
}

insert和erase

insert:这里是在pos位置插入一个字符。

erase:是删除从第pos位置后面的所有字符。

void string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}for (size_t i = 0;i < len;i++)//{_str[pos + i] = str[i];}_size += len;
}void string::erase(size_t pos = 0, size_t len = npos)
{assert(pos < _size);if (len > _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = len + pos;i <= _size;i++){_str[i - len] = _str[i];--_size;}//_size-=len;}
}

find和substr

find写了两个,一个是查找第一个字符,还有查找字符串。

substr:拷贝pos到len指定区域的字符。

size_t string::find(char ch, size_t pos)
{assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == ch){return i;}}return npos;
}
size_t string::find(const char* str, size_t pos)
{assert(pos < _size);const char* tmp = strstr(_str + pos, str);//str是比较字符串的函数。if (tmp == nullptr){return npos;}else{return tmp - _str;//?}}string string::substr(size_t pos, size_t len)//拷贝区间字符
{assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0;i < len;i++){sub[i] += _str[pos + i];}return sub;
}

operator+=

分为+=字符和字符串,可以直接调用插入函数。

string& string::operator+=(char ch)//这个函数是string类的呀,用域名修饰符修饰了,对的,这个函数实际还是一个成员函数,
{push_back(ch);return *this;//
}
string& string::operator+=(const char* str)
{append(str);return *this;//
}

operator <、>、==、<= 、>=、!=

我们可以通过实现其中两个来通过他们的逻辑关系来实现其他的。

我这里是实现了operator<和operator==,调用strcmp来实现的。

bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}

operator << 和 >>

operator<< 函数接受一个 ostream 类型的对象作为参数,并返回该对象本身。它遍历输入的字符串中的每个字符,并将其写入到输出流中。

operator>> 函数则接受一个 istream 类型的对象作为参数,并返回该对象本身。它首先清空输入字符串,然后逐个读取输入流中的字符,直到遇到空格或换行符为止。如果在读取过程中遇到了超过 N-1 个字符,则会将前面的部分存储到缓冲区中,并将剩余部分直接存储到输入字符串中。最后,它将读取到的所有字符存储到输入字符串中并返回输入流本身。

ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
istream& operator>>(istream& in, string& s)//
{s.clear();const int N = 256;char buff[N];int i = 0;char ch;//in >> ch;//使用in>>ch 默认空格为分格符读取不到ch = in.get();//给次对一个字符,什么都读。while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i > 0)//提前遇到“ ”或者 “\n”;{buff[i] = '\0';s += buff;}return in;
}

总结

感谢大家的支持,我会继续努力创造出更好的博客。

相关文章:

C++之“string”类的模拟实现

​ &#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;C入门 前言 hello &#xff0c;大家又来跟着bear学习了。一起奔向更好的自己&#xff0c;上篇博客已经讲清楚了string的一些功能的使用。我们就实现一些主要的功…...

请谈谈 HTTP 中的安全策略,如何防范常见的Web攻击(如XSS、CSRF)?

一、Web安全核心防御机制 &#xff08;一&#xff09;XSS攻击防御&#xff08;跨站脚本攻击&#xff09; 1. 原理与分类 ​存储型XSS&#xff1a;恶意脚本被持久化存储在服务端&#xff08;如数据库&#xff09;​反射型XSS&#xff1a;脚本通过URL参数或表单提交触发执行​…...

Python Flask 渲染静态程动态页面

Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 对网页应用程序来说&#xff0c;静态内容是重要的&#xff0c;因为它们包括 CSS 和 JavaScript 文件。静态文件可以直接由网页服务器提供。如果我们在我们的项目中创建一个…...

Unity大型游戏开发全流程指南

一、开发流程与核心步骤 1. 项目规划与设计阶段 需求分析 明确游戏类型&#xff08;MMORPG/开放世界/竞技等&#xff09;、核心玩法&#xff08;战斗/建造/社交&#xff09;、目标平台&#xff08;PC/移动/主机&#xff09;示例&#xff1a;MMORPG需规划角色成长树、副本Boss…...

Unity场景制作

一、关于后处理效果 然后可在后处理组件中添加各种效果 ACES : 电影感的强对比效果 添加了ACES后场景明显变暗&#xff0c;所以可以提高曝光度 Post-exposure 二、添加雾效 在Window的项目栏中选择Render中的Lighting 在环境属性中的其他设置中可勾选雾效&#xff0c;为场景中添…...

PCIE接口

PCIE接口 PIC接口介绍PIC总线结构PCI总线特点PCI总线的主要性能PIC的历程 PCIE接口介绍PCIe接口总线位宽PCIE速率GT/s和Gbps区别PCIE带宽计算 PCIE架构PCIe体系结构端到端的差分数据传递PCIe总线的层次结构事务层数据链路层物理层PCIe层级结构及功能框图 PCIe链路初始化PCIe链路…...

Leetcode 3479. Fruits Into Baskets III

Leetcode 3479. Fruits Into Baskets III 1. 解题思路2. 代码实现 题目链接&#xff1a;3479. Fruits Into Baskets III 1. 解题思路 这一题思路本质上就是考察每一个水果被考察时找到第一个满足条件且未被使用的basket。 因此&#xff0c;我们只需要将basket按照其capacit…...

小程序 -- uni-app开发微信小程序环境搭建(HBuilder X+微信开发者工具)

目录 前言 一 软件部分 1. 微信开发者工具 2. HBuilder X 开发工具 二 配置部分 1. 关于 HBuilder X 配置 2. 关于 微信开发工具 配置 三 运行项目 1. 新建项目 2. 代码编写 3. 内置浏览器 编译 4. 配置小程序 AppID获取 注意 四 实现效果 前言 uni-app开发小程…...

深度学习PyTorch之13种模型精度评估公式及调用方法

深度学习pytorch之22种损失函数数学公式和代码定义 深度学习pytorch之19种优化算法&#xff08;optimizer&#xff09;解析 深度学习pytorch之4种归一化方法&#xff08;Normalization&#xff09;原理公式解析和参数使用 深度学习pytorch之简单方法自定义9类卷积即插即用 实时…...

《云原生监控体系构建实录:从Prometheus到Grafana的观测革命》

PrometheusGrafana部署配置 Prometheus安装 下载Prometheus服务端 Download | PrometheusAn open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.https://prometheus.io/…...

GHCTF2025--Web

upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…...

NO.32十六届蓝桥杯备战|函数|库函数|自定义函数|实参|形参|传参(C++)

函数是什么 数学中我们其实就⻅过函数的概念&#xff0c;⽐如&#xff1a;⼀次函数 y kx b &#xff0c;k和b都是常数&#xff0c;给⼀个任意的x &#xff0c;就得到⼀个 y 值。其实在C/C语⾔中就引⼊了函数&#xff08;function&#xff09;的概念&#xff0c;有些翻译为&a…...

计算机视觉算法实战——老虎个体识别(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​ 1. 领域介绍 老虎个体识别是计算机视觉中的一个重要应用领域&#xff0c;旨在通过分析老虎的独特条纹图案&#xff0c;自动识别和区…...

【移动WEB开发】rem适配布局

目录 1. rem基础 2.媒体查询 2.1 语法规范 2.2 媒体查询rem 2.3 引入资源&#xff08;理解&#xff09; 3. less基础 3.1 维护css的弊端 3.2 less介绍 3.3 less变量 3.4 less编译 3.5 less嵌套 3.6 less运算 4. rem适配方案 4.1 rem实际开发 4.2 技术使用 4.3 …...

25年携程校招社招求职能力北森测评材料计算部分:备考要点与误区解析

在求职过程中&#xff0c;能力测评是筛选候选人的重要环节之一。对于携程这样的知名企业&#xff0c;其能力测评中的材料计算部分尤为关键。许多求职者在备考时容易陷入误区&#xff0c;导致在考试中表现不佳。本文将深入解析材料计算部分的实际考察方向&#xff0c;并提供针对…...

【Elasticsearch入门到落地】9、hotel数据结构分析

接上篇《8、RestClient操作索引库-基础介绍及导入demo》 上一篇我们介绍了RestClient的基础&#xff0c;并导入了使用Java语言编写的RestClient程序Demo以及将要分析的数据库。本篇我们就要分析导入的宾馆数据库tb_hotel表结构的具体含义&#xff0c;并分析如何建立其索引库。 …...

现代互联网网络安全与操作系统安全防御概要

现阶段国与国之间不用对方路由器&#xff0c;其实是有道理的&#xff0c;路由器破了&#xff0c;内网非常好攻击&#xff0c;内网共享开放端口也非常多&#xff0c;更容易攻击。还有些内存系统与pe系统自带浏览器都没有javascript脚本功能&#xff0c;也是有道理的&#xff0c;…...

轻量级TCC框架的实现

现有seata、tcc-transaction等tcc框架实现都较为重量级&#xff0c;今天主要带来一种轻量级的实现&#xff0c;大概只用了1200行代码实现。不依赖具体框架grpc、http、dubbo等&#xff0c;只需要业务系统按照标准化实现Try、Commit、Cancel实现接口即可。 已解决悬挂、幂等、空…...

共绘智慧升级,看永洪科技助力由由集团起航智慧征途

在数字化洪流汹涌澎湃的当下&#xff0c;企业如何乘风破浪&#xff0c;把握转型升级的黄金机遇&#xff0c;已成为所有企业必须直面的时代命题。由由集团&#xff0c;作为房地产的领航者&#xff0c;始终以前瞻视野引领变革&#xff0c;坚决拥抱数字化浪潮&#xff0c;携手数字…...

小程序开发总结

今年第一次帮别人做小程序。 从开始动手到完成上线&#xff0c;一共耗时两天。AI 让写代码变得简单、高效。 不过&#xff0c;小程序和 Flutter 等大厂开发框架差距实在太大&#xff0c;导致我一开始根本找不到感觉。 第一&#xff0c;IDE 不好用&#xff0c;各种功能杂糅在…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...