探索C++标准模板库(STL):String接口的底层实现(下篇)
前引:在C++的面向对象编程中,对象模型是理解语言行为的核心。无论是类的成员函数如何访问数据,还是资源管理如何自动化,其底层机制均围绕两个关键概念展开:this指针与六大默认成员函数。它们如同对象的“隐形守护者”,默默支撑着代码的健壮性与效率。本文将从技术底层出发,结合内存布局、编译器行为与实际案例,深入探讨!
本文目的:
在上篇我们学了各种string接口,此篇用来模拟实现各种接口功能,深入底层了解它的实现,来加深对它的理解,以及功能的熟悉
目录
String模拟实现
打印
访问字符
迭代器
插入字符/字符串
从pos位置插入字符
从pos位置插入字符串
从pos位置删除字符
从pos位置删除一定字符
寻找标记指定位置
截取指定区间
调整指定大小的空间
调整指定大小的空间+初始化
String接口模拟实现
打印
我们可以利用流提取来打印,返回字符串的指针就OK了,可以加上const更完美一点,如下:
//打印
const char* Read()const
{return _allocator;
}
cout提取到char *会默认当做字符串处理
访问字符
在库里面这种[ ]访问是有两个版本的,一种是只读的,一种是可读可写的,如下:
所以我们要实现两个版本,编译器也会自己去匹配对应的函数:
左边的const是防止返回的对象被修改;右边的const是防止this指针指向的对象被修改
const char& operator[](int size)const
{//检查assert(size <= _size && size >= 0);return _allocator[size];
}
char& operator[](int size)
{//检查assert(size <= _size && size >= 0);return _allocator[size];
}
迭代器
迭代器有两种形式,一种是从前往后访问,一种是从后往前访问,我们这里来实现第一种
其实iterator是char *类型的,只是在库里面被重定义了,然后实现 begin、end即可
iterator begin()
{return _allocator;
}
iterator end()
{return _allocator + _size;
}
const iterator begin()const
{return _allocator;
}
const iterator end()const
{return _allocator + _size;
}
效果展示:
auto 的底层其实就是迭代器,“换汤不换药”,例如:
插入字符/字符串
开空间:
插入字符/字符串只是插入的个数不同,所以我们可以根据插入的个数来选择扩容+拷贝原来字符串
//开空间
void reserve(size_t pos)
{char* tmp = nullptr;try{//开空间(传来的是最小空间,还要有\o)tmp = new char[pos + 1];}catch (const exception& _allocator){cout << "空间增容失败" << endl;}//拷贝原字符strcpy(tmp, _allocator);//添加\0size_t size = strlen(tmp);tmp[size + 1] = '\0';//转移指向delete[]_allocator;_allocator = tmp;
}
void push_back(char c)
{size_t pos = strlen(_allocator) + 1;reserve(pos);
}
void append(const char* stl)
{size_t pos = strlen(_allocator) + strlen(stl);reserve(pos);
}
存储:
根据传过来的字符/字符串在空间末尾添加上就行
//存储
void operator+=(char p1)
{_allocator[_size++] = p1;
}
void operator+=(const char* p2)
{strcpy(_allocator + _size, p2);
}
效果展示:
从pos位置插入字符
在某个位置插入字符很简单,我们来实现从pos位置插入 n 个字符
插入流程:
判断pos的合法性、开空间、移动原来字符、插入新字符
注意:
(1)在里面需要注意下标的使用,传的pos究竟是下标还是字符个数
(2)开空间需要注意给\0留位置,以及自动设置末尾的\0
(3)需要更新元素个数
//指定位置插入字符
void insert_t(int pos, int n , char c)
{//pos是个数不是下标//检查位置的合法性assert(pos >= 1 && pos <= _size);//开空间char* tmp = new char[n + _size +1 ];strcpy(tmp, _allocator);tmp[n + _size] = '\0';delete[]_allocator;_allocator = tmp;//更新_size_size += n;//移动原来的字符for (int i = (_size + n); (i-n) >= pos ;i--){_allocator[i - 1] = _allocator[i - n - 1];}//插入新字符for (int i = pos; n > 0; i++){_allocator[i - 1] = c;n--;}
}
效果展示:
从pos位置插入字符串
这个较于插入字符比较简单一些,开空间、插入字符串都没有什么大的变化,如下:
这种需要调试的多,重要的是下标关系,新手可能需要在草稿纸上演示
//指定位置插入字符串
void insert(int pos, const char* _stl)
{//pos是个数不是下标//检查位置的合法性assert(pos >= 1 && pos <= _size);//开空间int n = strlen(_stl);char* tmp = new char[n + _size + 1];strcpy(tmp, _allocator);tmp[n + _size] = '\0';delete[]_allocator;_allocator = tmp;//更新_size_size += n;//移动原来的字符for (int i = (_size ); (i - n) >= pos; i--){_allocator[i - 1] = _allocator[i - n - 1];}//插入新字符int j = 0;for (int i = pos; n > 0; i++){_allocator[i-1] = _stl[j++];n--;}
}
从pos位置删除字符
模拟成员函数:
删除字符不需要去开空间,可以在目标位置插入\0即可,记得更新字符个数
//指定位置删除字符
void erase(size_t pos)
{//判断位置有效性assert(pos >= 1 && pos <= _size);//删除_allocator[pos - 1] = '\0';_size--;
}
从pos位置删除一定字符
这个接口的实现需要挪动后面的元素,整体来说还是很简单的
注意:更新字符个数,新字符串的末尾添加\0
void erase_t(size_t pos, size_t n)
{//判断位置有效性assert(pos >= 1 && (pos+n-1)<=_size);_size -= n;//挪动元素int i = 1;for (i = pos ; _allocator[i + n -1] != '\0' ; i++){_allocator[i - 1] = _allocator[i + n - 1];}//插上\0_allocator[i-1] = '\0';
}
寻找标记指定位置
模拟库函数:
此函数有多种形式,我们来实现最简单的一种:
给一个位置,从此位置开始寻找目标,如果找到了就返回目标开始的下标位置,否则返回npos
实现思路:
假设给了一个字符串,和开始寻找的位置,从开始位置开始搜寻,遇到可能相同的目标开始确认,找到则返回开始的下标位置,否则返回npos(npos是无符号整型的最大值)
//找指定字符串
size_t find(size_t pos, const char* _stl)
{//判断位置的合法性assert(pos >= 1 && pos <= _size);//从指定位置开始找目标const char* pc = _stl;while (1){//找到可能目标if (_allocator[pos - 1] == *_stl){//确认目标int tmp = pos;pc = _stl;while (pc){//开始逐一比较字符if (*pc == _allocator[tmp - 1]){pc++;if (*pc == '\0'){return pos;}tmp++;if (pos >= _size){return npos;}}else{//如果不相等pos++;tmp = pos;break;}}}else{//如果没有找到,就继续,直到pos++;if (pos >= _size){return npos;}}}return npos;
}
效果展示:
截取指定区间
此函数通常是与上面标记的函数同时使用,由标记的函数先找指定位置,然后再截取,需要注意给的区间的有效性
原理:
截取一端区间打印即可
//截取指定位置
void substr(size_t p1, size_t p2)
{//判断范围assert(p2 <= _size && p1 >= 1);size_t pos = 1;//找左区间while (_allocator[p1 - 1] != _allocator[pos-1]){pos++;}//开始打印while ((pos-1) != p2){cout << _allocator[pos-1];pos++;}cout << endl;
}
效果展示:
调整指定大小的空间
我们模拟的resize,输入空间大小,将自动开辟指定大小的空间,没有初始化操作
//开空间
void reserve(size_t pos)
{char* tmp = nullptr;try{//开空间(传来的是最小空间,还要有\o)tmp = new char[pos + 1];}catch (const exception& _allocator){cout << "空间增容失败" << endl;}//拷贝原字符strcpy(tmp, _allocator);//添加\0size_t size = strlen(tmp);tmp[pos] = '\0';//转移指向delete[]_allocator;_allocator = tmp;
}
调整指定大小的空间+初始化
调整规则:
如果 size_t 大于当前字符串长度,则将剩余的字符初始化为指定的内容,否则转为空格
如果 size_t 小于当前字符串长度,则缩短为其前 n 个字符,并删除超出第 n 个字符的字符
如果小于_size直接缩短即可,如果大于我们又要麻烦的分情况,所以我们统一处理为开空间
void resize(size_t n, char c = '\0')
{//对n判断assert(n >= 1);//判断n的大小if (n < _size){//缩短字符串_allocator[n - 1] = '\0';}else{//开空间reserve(n);//初始化for (size_t i = _size; i < n; i++){_allocator[i] = c;}}
}
效果展示:
【雾非雾】期待与你的下次相遇!
相关文章:

探索C++标准模板库(STL):String接口的底层实现(下篇)
前引:在C的面向对象编程中,对象模型是理解语言行为的核心。无论是类的成员函数如何访问数据,还是资源管理如何自动化,其底层机制均围绕两个关键概念展开:this指针与六大默认成员函数。它们如同对象的“隐形守护者”&am…...

Flutter知识点汇总
Flutter架构解析 1. Flutter 是什么?它与其他移动开发框架有什么不同? Flutter 是 Google 开发的开源移动应用开发框架,可用于快速构建高性能、高保真的移动应用(iOS 和 Android),也支持 Web、桌面和嵌入式设备。。它与其他移动开发框架(如 React Native、Xamarin、原…...
vue组件的data为什么是函数?
vue组件的data为什么是函数? 在JS中,实例是通过构造函数创建的,每个构造函数可以new出多个实例,每个实例都会继承原型上的方法和属性。 在vue中,一个vue组件就是一个实例,当一个组件被复用多次࿰…...
AI图片售卖:是暴利新风口还是虚幻泡沫?哪些平台适合售卖AI图片
还记得去年大火的Midjourney吗?今年4月,Midjourney又发布了备受期待的V7版本,带来了更高的图像质量和创新功能。使用Midjourney、Stable Diffusion、DALLE等AI图片生成工具,创作者只需输入关键词即可获得高质量的原创图片。这一变…...

线性注意力 vs. 传统注意力:效率与表达的博弈新解
核心结论:线性注意力用计算复杂度降维换取全局建模能力,通过核函数和结构优化补足表达缺陷 一、本质差异:两种注意力如何工作? 特性传统注意力(Softmax Attention)线性注意力(Linear At…...

YOLO在QT中的完整训练、验证与部署方案
以下是YOLO在QT中的完整训练、验证与部署方案: 训练方案 准备数据集: 收集数据:收集与目标检测任务相关的图像数据集,可以是公开数据集如COCO、Pascal VOC,也可以是自定义数据集。标注数据:使用标注工具如…...
Vue在线预览excel、word、ppt等格式数据。
目录 前言 1.安装库 2.预览文件子组件代码 3、新建store/system.ts 4、父页面进行使用 总结 前言 纯前端处理文件预览,包含excel、word、ppt、txt等格式,不需要后端服务器进行部署,并且内网也可以使用。 1.安装库 npm install vue-offi…...

增量式网络爬虫通用模板
之前做过一个项目,他要求是只爬取新产生的或者已经更新的页面,避免重复爬取未变化的页面,从而节省资源和时间。这里我需要设计一个增量式网络爬虫的通用模板。可以继承该类并重写部分方法以实现特定的解析和数据处理逻辑。这样可以更好的节约…...

【JVM】三色标记法原理
在JVM中,三色标记法是GC过程中对象状态的判断依据,回收前给对象设置上不同的三种颜色,三色分为白色、灰色、黑色。根据颜色的不同,决定对象是否要被回收。 白色表示: 初始状态:所有对象未被 GC 访问。含义…...

【uniapp开发】picker组件的使用
项目uniapp,结合fastadmin后端开发 picker组件的官方文档说明 https://en.uniapp.dcloud.io/component/picker.html#普通选择器 先看效果: 1、实现设备类型的筛选;2、实现设备状态的筛选; 前端代码(节选࿰…...

【HarmonyOS Next之旅】DevEco Studio使用指南(三十一) -> 同步云端代码至DevEco Studio工程
目录 1 -> 同步云函数/云对象 1.1 -> 同步单个云函数/云对象 1.2 -> 批量同步云函数/云对象 2 -> 同步云数据库 2.1 -> 同步单个对象类型 2.2 -> 批量同步对象类型 3 -> 一键同步云侧代码 1 -> 同步云函数/云对象 说明 对于使用DevEco Studio…...
如何评估大语言模型效果
评估大模型微调后的效果是一个系统化的过程,需要结合客观指标和主观评估,并根据任务类型(分类、生成、回归等)选择合适的评估方法。 一、评估前的准备工作 数据集划分: 将数据分为 训练集、验证集 和 测试集ÿ…...

go-zero微服务入门案例
一、go-zero微服务环境安装 1、go-zero脚手架的安装 go install github.com/zeromicro/go-zero/tools/goctllatest2、etcd的安装下载地址根据自己电脑操作系统下载对应的版本,具体的使用自己查阅文章 二、创建一个user-rpc服务 1、定义user.proto文件 syntax &qu…...

Python控制台输出彩色字体指南
在Python开发中,有时我们需要在控制台输出彩色文本以提高可读性或创建更友好的用户界面。本文将介绍如何使用colorama库来实现这一功能。 为什么需要彩色输出? 提高可读性:重要信息可以用不同颜色突出显示更好的用户体验:错误信息…...
零基础在实践中学习网络安全-皮卡丘靶场(第十六期-SSRF模块)
最后一期了,感谢大家一直以来的关注,如果您对本系列文章内容有问题或者有更好的方法,请在评论区发送。 介绍 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制导致攻击者可以传入任意…...

开源之夏·西安电子科技大学站精彩回顾:OpenTiny开源技术下沉校园,点燃高校开发者技术热情
开源之夏2025编程活动正在如火如荼的进行中,当前也迎来了报名的倒计时阶段,开源之夏组织方也通过高校行系列活动进入各大高校,帮助高校开发者科普开源文化、开源活动、开源技术。 6月4日 开源之夏携手多位开源技术大咖、经验型选手走进西安电…...
html、css(javaweb第一天)
HTML: 文字、图片、视频组成 由标签组成的语言 行内标签span//无语意 <img src"url">//图片 <a herf"url" target"是否开新页面">点击谁</a>//超链接 <video src"url" controls></video>//controls播放…...

解决数据库重启问题
最近部署软件时,发现mysql会一直在重启,记录下解决办法: 1.删除/home/dataexa/install/docker/datas/mysql路径下的data文件夹 2.重新构建mysql docker-compose up -d --build mysql 3.停掉所有应用,在全部重启: do…...

前后端交互过程中—各类文件/图片的上传、下载、显示转换
前后端交互过程中—各类文件/图片的上传、下载、显示转换 图片补充:new Blob()URL.createObjectURL()替代方案:FileReader.readAsDataURL()对比: tiff文件TIFF库TIFF转换通过url转换tiff文件为png通过文件选择的方式转换tiff文件为png 下…...
SparkSQL 优化实操
一、基础优化配置 1. 资源配置优化 # 提交Spark作业时的资源配置示例 spark-submit \--master yarn \--executor-memory 8G \--executor-cores 4 \--num-executors 10 \--conf spark.sql.shuffle.partitions200 \your_spark_app.py 参数说明: executor-memory: 每…...
【vLLM 学习】Cpu Offload Lmcache
vLLM 是一款专为大语言模型推理加速而设计的框架,实现了 KV 缓存内存几乎零浪费,解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ *在线运行 vLLM 入门教程:零基础分步指南 源码 examples/offline_inf…...

数据库同步是什么意思?数据库架构有哪些?
目录 一、数据库同步是什么 (一)基本概念 (二)数据库同步的类型 (三)数据库同步的实现方式 二、数据库架构的类型 (一)单机架构 (二)主从复制架构 &a…...

【数据结构】详解算法复杂度:时间复杂度和空间复杂度
🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》、《数据结构与算法》 🍉学习方向:C/C方向 ⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平 前言&…...

Rest-Assured API 测试:基于 Java 和 TestNG 的接口自动化测试
1. 右键点击项目的文件夹,选择 New > File。 2. 输入文件名,例如 notes.md,然后点击 OK。 3. 选择项目类型 在左侧的 Generators 部分,选择 Maven Archetype,这将为你生成一个基于 Maven 的项目。 4. 配置项目基…...
多模型协同:基于 SAM 分割 + YOLO 检测 + ResNet 分类的工业开关状态实时监控方案
一、技术优势与适配性分析 1. 任务分工的合理性 YOLO(目标检测) 核心价值:快速定位工业开关在图像中的位置(边界框),为后续分割和分类提供ROI(感兴趣区域)。工业场景适配性…...
【分销系统商城】
分销商城系统是一种结合电商与社交裂变的多层级分销管理平台,通过佣金激励用户成为分销商,实现低成本快速拓客和销量增长。以下是其核心要点解析: 🛍️ 一、系统定义与核心价值 基本概念 核心模式&#…...
LangChainGo入门指南:Go语言实现与OpenAI/Qwen模型集成实战
目录 1、什么是langchainGo2、langchainGo的官方地址3、LangChainGo with OpenAI3-1、前置准备3-2、安装依赖库3-3、新建模型客户端3-4、使用模型进行对话 4、总结 1、什么是langchainGo langchaingo是langchain的go语言实现版本 2、langchainGo的官方地址 官网:…...
5.1 HarmonyOS NEXT系统级性能调优:内核调度、I/O优化与多线程管理实战
HarmonyOS NEXT系统级性能调优:内核调度、I/O优化与多线程管理实战 在HarmonyOS NEXT的全场景生态中,系统级性能调优是构建流畅、高效应用的关键。通过内核调度精细化控制、存储与网络I/O深度优化,以及多线程资源智能管理,开发者…...

react public/index.html文件使用env里面的变量
env文件 ENVdevelopment NODE_ENVdevelopment REACT_APP_URL#{REACT_APP_URL}# REACT_APP_CLIENTID#{REACT_APP_CLIENTID}# REACT_APP_TENANTID#{REACT_APP_TENANTID}# REACT_APP_REDIRECTURL#{REACT_APP_REDIRECTURL}# REACT_APP_DOMAIN_SCRIPT#{REACT_APP_DOMAIN_SCRIPT}#pu…...

chili3d 笔记17 c++ 编译hlr 带隐藏线工程图
这个要注册不然emscripten编译不起来 --------------- 行不通 ---------------- 结构体 using LineSegment std::pair<gp_Pnt, gp_Pnt>;using LineSegmentList std::vector<LineSegment>; EMSCRIPTEN_BINDINGS(Shape_Projection) {value_object<LineSegment&g…...