std::string的模拟实现
目录
string的构造函数
无参数的构造函数
根据字符串初始化
用n个ch字符初始化
用一个字符串的前n个初始化
拷贝构造
用另一个string对象的pos位置向后len的长度初始化
[ ]解引用重载
迭代器的实现
非const版本
const版本
扩容reserve和resize
reserve
resize
push_back和append
push_back
append
+=运算符重载
insert插入
erase删除
find
substr
流插入和流提取
比较
赋值运算符重载
补充
string是库中用于储存字符串的类,其底层实际上是顺序表。
namespace cfl
{class string{public:private:char* _str; //指向储存字符串的起始位置size_t _size; //存储有效字符个数size_t _capacity; //存储开辟的空间总个数static size_t npos;};size_t string::npos = -1;
}
string中有一个静态变量是npos类型是size_t,但是定义是却定义为-1,所以npos强转为了无符号整形,也就是能存储size_t类型的最大值,其用来表示整个字符串。
string的构造函数
参考文献:string::string - C++ Reference
https://legacy.cplusplus.com/reference/string/string/string/
string的构造一共有中其中有一种的实现需要迭代器,在下面实现迭代器的时候讲解,先完成前6种。
无参数的构造函数
//无参数的构造函数
string():_str(new char('\0')) //此处不直接设置nullptr,将其设置为空字符串,_size(0),_capacity(0)
{}
根据字符串初始化
//根据字符串初始化的构造函数
string(const char* str):_size(strlen(str)), _capacity(strlen(str))
{_str = new char[_size]; //此处也可以在上面进行初始化,但是要注意顺序 memcpy(_str, str, _size + 1); //_size+1是要拷贝'\0'
}
用n个ch字符初始化
//用n个ch字符初始化的构造函数
string(size_t n, const char ch):_str(new char[n+1]) //要开辟n+1个空间是为了存储'\0', _size(n), _capacity(n)
{memset(_str,ch, n);_str[_size] = '\0';
}
用一个字符串的前n个初始化
//用一个字符串的前n个初始化的构造函数
string(const char* str, size_t n):_str(new char[n + 1]), _size(n), _capacity(n)
{memcpy(_str, str, n);_str[_size] = '\0';
}
拷贝构造
//拷贝构造函数
string(const string& s)
{_capacity = s._capacity;_str = new char[_capacity + 1];_size = s._size;memcpy(_str, s._str, _size);_str[_size] = '\0';
}
用另一个string对象的pos位置向后len的长度初始化
//用另一个string对象的pos位置向后len的长度初始化的构造函数
string(const string& s, size_t pos, size_t len = npos) //len给一个缺省值是npos,代表整个字符串
{if (len == npos || len + pos >= s._size){len = s._size - pos;_capacity=len;_str = new char[_capacity + 1];_size = len;_str[_size] = '\0';}else{_capacity = len;_str = new char[_capacity + 1];_size = len;_str[_size] = '\0';}
}
[ ]解引用重载
string类对象可以像数组一样通过下标访问是因为解引用运算符重载。
char& operator[](size_t pos)
{return _str[pos];
}
迭代器的实现
迭代器实际上可以看成一个指针,但是其与指针有一些区别。范围for的底层逻辑就是迭代器,实现了迭代器,范围for才能使用。
非const版本
//迭代器的实现
typedef char* iterator;
typedef const char* const_iterator;
iterator begin() //begin就是返回起始位置的指针
{return _str;
}
iterator end()
{return _str + _size; //返回最后一个字符下一个位置。
}
const版本
const_iterator begin()const
{return _str;
}
const_iterator end()const
{return _str + _size;
}
扩容reserve和resize
reserve
reserve扩容要帮助,传过去的参数比当前空间大才扩容,比当前空间小则不做处理。
void reserve(size_t n)
{if(n>_capacity){//先开辟新的空间,再将数据拷贝回来char* tmp= new char[n + 1];memcpy(tmp, _str, _size + 1);_str = tmp;_capacity = n;}
}
resize
resize和reverse扩容有所区别,传给resize的空间小于_size会将n后面的所有数据删除,resize还会对像开辟的空间进行初始化。
void resize(size_t n, char ch = '\0')
{if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (int i = _size; i < n; i++)_str[i] = ch;_str[_capacity] = '\0';}
}
push_back和append
push_back
尾插直接向顺序表结尾插入数据,尾插是也要考虑扩容。
void push_back(char ch)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';
}
append
appened是直接在尾部插入字符串。
void append(const char* s)
{size_t len = strlen(s);if (_capacity == _size || len + _size > _capacity){reserve(len + _size);}memcpy(_str + _size, s, len + 1);_size = _size + len;
}
+=运算符重载
string& operator+=(const char ch)
{push_back(ch);return *this;
}string& operator+=(const char* s)
{append(s);return *this;
}
insert插入
insert有两种:在pos位置插入n个字符ch,在pos插入字符串。
void insert(size_t pos, size_t n, char ch)
{assert(pos < _size);//判断空间够不够if (n + _size < _capacity){reserve(n + _size);}//将数据向后移size_t end = _size;while (end >= pos){_str[end + n] = _str[end];--end;}//插入数据for (int i = 0; i < n; i++)_str[pos + i] = ch;
}
void insert(size_t pos, const char* str)
{assert(pos < _size);size_t len = strlen(str);if (_size == _capacity || len + _size > _capacity){reserve(len + _size);}int end = _size;while (end >= pos){_str[end + len] = _str[end];--end;}for (int i = 0; i < len; i++)_str[pos + i] = str[i];
}
erase删除
从pos位置删除len的长度。
void erase(size_t pos, size_t len = npos)
{if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{int end = pos+len;while (end < _size){_str[end - len] = _str[end];++end;}_size -= len;_str[_size] = '\0';}
}
find
从pos位置向后找字符ch。
size_t find(size_t pos, const char ch)
{assert(pos < _size);int cur = pos;while (cur < _size){if (_str[cur] == ch)return cur;++cur;}return npos; //表示没找到
}
substr
返回冲pos位置开始取n个字符的子字符串。
string substr(size_t pos, size_t n)
{int end = pos + n;if (end > _size){end = _size;}string s;for (int i = pos; i < end; i++)s += _str[i];return s;
}
流插入和流提取
流插入和流提取要放在类外定义,具体原因在<类和对象>该文种有讲解。
//流插入
ostream& operator<<(ostream& out, const cfl::string& s)
{for (auto e : s)out << e; //使用循环for可以防止s中间有\0导致停止return out;
}
istream& operator>>(istream& in, cfl::string& s)
{//每次插入一个字符会导致每次尾插都要开空间//设置一个数组,先将字符插入到数组中,再将数组插入到s中char tmp[128];char ch = in.get();//此处使用in.get可以读取输入流中的空格int i = 0;while (ch != '\n'){tmp[i++] = ch;if (i == 127){tmp[127] = '\0';i = 0;s += tmp;}ch = in.get();}tmp[i] = '\0';s += tmp;return in;
}
注意:流插入重载和流提取重载都是string的友元函数。
比较
此处仅实现>和==,其他比较可以直接套用他们两个。
bool operator<(const string& s)
{size_t end1 = _size;size_t end2 = s._size;int i = 0;while (i < end1 && i < end2){if (_str[i] < s._str[i])return true;else if (_str[i] > s._str[i])return false;i++;}if (i < end1)return true;return false;
}bool operator==(const string& s)
{if (_size != s._size)return false;int i = 0;while (i < _size){if (_str[i] != s._str[i])return false;i++;}return true;
}
赋值运算符重载
void swap(string& s)
{std::swap(this->_str, s._str);std::swap(this->_capacity, s._capacity);std::swap(this->_size, s._size);
}string& operator=(string tmp)
{swap(tmp); //此处的tmp是临时变量,又是原对象的拷贝//所以直接将tmp与*this,交换,实现赋值//因为tmp是临时变量出作用域后会将*this的空间释放return *this;
}
补充
补充一些不是太常用的string的成员函数。
1)string::shrink_to_fit()由于将空间缩至有效数据处;
2)string::at(size_t n)返回n处的字符,相当于s1[n];
3)string::replace()替换类中的数据;
4)string::rfind()查找,但是是从后往前找;
5)size_t find_first_of(string& str,size_t pos=0),从pos位置向后找,找到str中的任意一个字符就返回;
6)size_t find_end_of(string& str,size_t pos=npos),已5)不同的是该函数从后往前找:
7)getline(cin,str),读取流中的数据,可指定读取到什么的时候停止,默认是换行符;
8)string to_string()可以将非字符串转化为字符串。
相关文章:
std::string的模拟实现
目录 string的构造函数 无参数的构造函数 根据字符串初始化 用n个ch字符初始化 用一个字符串的前n个初始化 拷贝构造 用另一个string对象的pos位置向后len的长度初始化 [ ]解引用重载 迭代器的实现 非const版本 const版本 扩容reserve和resize reserve resize p…...
wordpress自定the_category的输出结构
通过WordPress的过滤器the_category来自定义输出内容。方法很简单,但是很实用。以下是一个示例代码: function custom_the_category($thelist, $separator , $parents ) {// 获取当前文章的所有分类$categories get_the_category();if (empty($categ…...
doris: Oracle
Apache Doris JDBC Catalog 支持通过标准 JDBC 接口连接 Oracle 数据库。本文档介绍如何配置 Oracle 数据库连接。 使用须知 要连接到 Oracle 数据库,您需要 Oracle 19c, 18c, 12c, 11g 或 10g。 Oracle 数据库的 JDBC 驱动程序,您可以从 Maven 仓库…...
mysql中什么机制保证宕机数据恢复
MySQL 通过多种机制来保证在宕机或意外崩溃时数据的完整性和可恢复性。这些机制主要包括 事务日志、崩溃恢复 和 数据持久化 等。以下是 MySQL 中保证数据恢复的核心机制: 1. 事务日志(Transaction Log) 事务日志是 MySQL 实现数据恢复的核心机制之一,主要包括 Redo Log(…...
前端面试技术性场景题
87.场景面试之大数运算:超过js中number最大值的数怎么处理 在 JavaScript 中,Number.MAX_SAFE_INTEGER(即 2^53 - 1,即 9007199254740991)是能被安全表示的最大整数。超过此值时,普通的 Number 类型会出现…...
解决CentOS 8.5被恶意扫描的问题
CentOS 8 官方仓库已停止维护(EOL),导致一些常用依赖包如fail2ban 无法正常安装。 完整解决方案: 一、问题根源 CentOS 8 官方仓库已停更:2021 年底 CentOS 8 停止维护,默认仓库的包可能无法满足依赖关系。EPEL 仓库兼容性:EPEL 仓库可能未适配 CentOS 8.5 的旧版本依赖…...
探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)
文章目录 2.3.3 极化编码巴氏参数与信道可靠性比特混合生成矩阵编码举例 2.3.4 极化译码最小单元译码串行抵消译码(SC译码)算法SCL译码算法 2.3.5 总结**Polar 码的优势****Polar 码的主要问题****Polar 码的应用前景** 2.3.6 **参考文档** 本博客为系列…...
机器学习准备工作
机器学习准备工作 机器学习概述常见算法动手实践 深度学习基础框架应用领域 数学基础线性代数概率论和统计学微积分 编程基础Python基础数据处理工具 项目实战入门1. Scikit-learn 示例项目2. TensorFlow/Keras 入门项目3. Kaggle 入门竞赛 进阶1. PyTorch 官方教程2. MMDetect…...
汽车智能钥匙中PKE低频天线的作用
PKE(Passive Keyless Entry)即被动式无钥匙进入系统,汽车智能钥匙中PKE低频天线在现代汽车的智能功能和安全保障方面发挥着关键作用,以下是其具体作用: 信号交互与身份认证 低频信号接收:当车主靠近车辆时…...
Codepen和tailwindcss 进行UI布局展示
html <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>设备管理仪表盘</title><script src"https://cdn.tailw…...
准备好了数据集之后,如何在ubuntu22.04上训练一个yolov8模型。
在Ubuntu 22.04上训练YOLOv8模型的步骤如下: 1. 安装依赖 首先,确保系统已安装Python和必要的库。 sudo apt update sudo apt install python3-pip python3-venv2. 创建虚拟环境 创建并激活虚拟环境: python3 -m venv yolov8_env source…...
集合框架、Collection、list、ArrayList、Set、HashSet和LinkedHashSet、判断两个对象是否相等
DAY7.1 Java核心基础 集合框架 Java 中很重要的一个知识点,实际开发中使用的频录较高,Java 程序中必备的模块 集合就是长度可以改变,可以保存任意数据类型的动态数组 最上层是一组接口,接下来是接口的实现类,第三层…...
宝塔 Linux 计划任务中添加运行项目网站PHP任务-定时任务
一、指定php版运行, cd /www/wwwroot/www.xxx.com/ && /www/server/php/56/bin/php think timedtasks start >> /tmp/timedtasks.log 2>&1 二、不指定php版 cd /www/wwwroot/www.xxx.com/ && php think timedtasks start >> …...
JDK ZOOKEEPER KAFKA安装
JDK17下载安装 mkdir -p /usr/local/develop cd /usr/local/develop 将下载的包上传服务器指定路径 解压文件 tar -zxvf jdk-17.0.14_linux-x64_bin.tar.gz -C /usr/local/develop/ 修改文件夹名 mv /usr/local/develop/jdk-17.0.14 /usr/local/develop/java17 配置环境变量…...
c++雅兰亭库 (yalantinglibs) 介绍及使用(序列化、json和结构体转换、协程
c雅兰亭库 (yalantinglibs) 介绍及使用(序列化、json和结构体转换、协程)-CSDN博客 雅兰亭库(yalantinglibs)介绍 雅兰亭库,名字很优雅,也很强大。它是阿里开源的一个现代C基础工具库的集合, 现在包括 struct_pack, struct_json, struct_xml, struct_yam…...
深度融合,智领未来丨zAIoT 全面集成 DeepSeek,助力企业迎接数据智能新时代
前言 Introduction 在数字化浪潮汹涌澎湃的当下,数据智能成为企业破局与创新的关键驱动力。zAIoT 作为云和恩墨面向 AIData 时代推出的数据智能平台软件,凭借其全面且强大的“采存算用”一体化功能体系,正在为航空航天、工业制造等领域和态势…...
类和对象—多态—案例2—制作饮品
案例描述: 制作饮品的大致流程为:煮水-冲泡-倒入杯中-加入辅料 利用多态技术实现本案例,提供抽象制作产品基类,提供子类制作咖啡和茶叶 思路解析: 1. 定义抽象基类 - 创建 AbstractDrinking 抽象类,该类…...
VSCode 配置优化指南:打造高效的 uni-app、Vue2/3、JS/TS 开发环境
VSCode 配置优化指南,适用于 uni-app、Vue2、Vue3、JavaScript、TypeScript 开发,包括插件推荐、设置优化、代码片段、调试配置等,确保你的开发体验更加流畅高效。 1. 安装 VSCode 如果你还未安装 VSCode,可前往 VSCode 官网 下载最新版并安装。 2. 安装推荐插件 (1) Vue…...
低代码平台的后端架构设计与核心技术解析
引言:低代码如何颠覆传统后端开发? 在传统开发模式下,一个简单用户管理系统的后端开发需要: 3天数据库设计5天REST API开发2天权限模块对接50个易出错的代码文件 而现代低代码平台通过可视化建模自动化生成,可将开发…...
Redis中多大的Key算热key,该如何解决
在 Redis 中,“热key” 是指频繁访问的 Redis 键。这些键通常会导致 Redis 服务器的性能下降,甚至可能导致 Redis 服务不可用。热key 的大小是相对的,通常来说,以下几个因素可能导致一个 Redis 键成为热key: 访问频率…...
机器学习数学基础:43.外生变量与内生变量
外生变量与内生变量:模型中的因果角色 在因果模型(像结构方程模型、回归分析这类)里,外生变量和内生变量是用来区分变量来源和相互关系的重要概念。下面从定义、实例、差异以及应用场景四个方面来详细介绍: 一、定义…...
单元测试与仿真程序之间的选择
为什么写这篇文章 现在的工作需求,让我有必要总结和整理一下。 凡事都有适用的场景。首先这里我需要提示一下,这里的信息,可能并不普适。 但是可以肯定一点的是,有些人,不论做事还是写书,上下文还没有交待…...
一周学会Flask3 Python Web开发-SQLAlchemy简介及安装
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射(ORM)工具,…...
【玩转正则表达式】正则表达式常用语法汇总
1. 基本字符 普通字符:匹配自身。例如,正则表达式hello匹配字符串中的“hello”。\d:匹配任何数字字符,相当于[0-9]。例如,\d\d\d匹配三个连续的数字。 示例:123、456 \w:匹配任何字母数字字符…...
django中序列化器serializer 的高级使用和需要注意的点
在 Django REST framework(DRF)中,序列化器(Serializer)是一个强大的工具,用于将复杂的数据类型(如 Django 模型实例)转换为 Python 原生数据类型,以便将其渲染为 JSON、XML 等格式,同时也能将接收到的外部数据反序列化为 Django 模型实例。以下将介绍序列化器的高级…...
如何下载安装 PyCharm?
李升伟 整理 一、下载 PyCharm 访问官网 打开 PyCharm 官网,点击 "Download" 按钮25。 版本选择: 社区版(Community):免费使用,适合个人学习和基础开发。 专业版(Professional&#…...
URL中的特殊字符与web安全
在现代Web应用中,URL作为客户端与服务器之间的通信桥梁,承载着大量的重要信息。URL中的特殊字符,看似只是一些常见的符号,但在Web安全领域,它们与其他安全知识密切相关,如在Base64编码、SQL注入,…...
Golang学习笔记_41——观察者模式
Golang学习笔记_38——享元模式 Golang学习笔记_39——策略模式 Golang学习笔记_40——模版方法模式 文章目录 一、核心概念1. 定义2. 解决的问题3. 核心角色4. 类图 二、特点分析三、适用场景1. 股票价格监控系统2. 物联网设备状态监控3. 电商订单状态通知 四、Go语言实现示例…...
中原银行:从“小机+传统数据库”升级为“OceanBase+通用服务器”,30 +系统成功上线|OceanBase DB大咖说(十五)
OceanBase《DB 大咖说》第 15 期,我们邀请到了中原银行金融科技部数据团队负责人,吕春雷。本文为本期大咖说的精选。 吕春雷是一位资历深厚的数据库专家,从传统制造企业、IT企业、甲骨文公司到中原银行,他在数据库技术与运维管理…...
slam学习笔记9---ubuntu2004部署interactive_slam踩坑记录
背景:interactive_slam是一款可用于离线优化点云地图算法。部署安装容易出问题,这里记录一下。 一、安装基本流程 绝大部分跟着readme走,g2o安装使用apt安装 interactive_slam depends on the following libraries:GL3W GLFW Dear ImGui p…...
