探索C++标准模板库(STL):String接口实践+底层的模拟实现(中篇)

 

前引:上一篇文章小编已经整理出了String的常用接口,梳理了各个接口的功能、参数,如何使用等各种实例。本篇文章将带大家看看String这些接口的实践使用,探索这些接口的实用性,是如何增加代码效率的。在本篇文章的末尾,还奉上了部分底层的模拟实现,String类的使用是有趣的,下面我们来从实践中感受String类带给我们的快捷、效率!

目录

string类的模拟实现

构造初始化

析构函数

流提取

流插入

大小比较

拷贝构造

赋值运算符重载


string类的模拟实现

下面我们来实现String的底层,解读String的原理:

std库里面的String有它的专属空间,也就是C++库

下面我们来命名自己的空间,同时String是一个类,我们需要实现:自定义空间+一个类

namespace Space
{class string{};
}
构造初始化

观察库里面的 string 初始化特点:

可以看到它的变量有三个:size、capacity、字符空间

下面我们来自己实现它的构造初始化:

初始化size、capacity、空间、存储

//自定义构造
string(const char* allocator):_size(strlen(allocator)),_capacity(2*_size+1)
{try{_allocator = new char[_capacity];}catch (const exception& _allocator){cout << "空间开辟失败" << endl;}//存储strcpy(_allocator, allocator);
}

效果展示:

 可以看到是没有问题的,但是我们平常是可能按下面的方式初始化的:

string S;

所以我们还得再写一个无参默认构造,如下:

注意:不能是全缺省的,否则参数相同,编译器无法区分

//默认构造
string():_size(0), _capacity(10)
{try{_allocator = new char[_capacity];}catch (const exception& _allocator){cout << "空间开辟失败" << endl;}
}

以上我们的构造初始化就写好了,可以随时应对各种初始化情况,总代码如下:

namespace Space
{class string{public://默认构造string():_size(0), _capacity(10){try{_allocator = new char[_capacity];}catch (const exception& _allocator){cout << "空间开辟失败" << endl;}}//自定义构造string(const char* allocator):_size(strlen(allocator)),_capacity(2*_size+1){try{_allocator = new char[_capacity];}catch (const exception& _allocator){cout << "空间开辟失败" << endl;}//存储strcpy(_allocator, allocator);}private:size_t _size;size_t _capacity;char* _allocator;};
}
析构函数

这个函数很简单,释放空间,改变 size、capacity这些就可以了,如下:

//析构
~string()
{delete[]_allocator;_size = 0;_capacity = 0;cout << "释放成功" << endl;
}
流提取

在上面我们已经实现了读取函数,但是追求方便,且两者有很大区别,比如: 

cout << S1.Read() << endl;
cout << S1 << endl;

ostream& operator<<(ostream& out, const string& _stl) //没有找命名空间里面
{for (auto ac : _stl){cout << ac;}return out;
}

注意:不能在成员函数中实现,因为this指针会抢占第一个操作符位置,所以我们放在外面实现

区别:

C的字符数组,以\0为终止算长度

String不看\0,以size为终止长度,例如:

这样看虽然没有什么区别,但是如果添加上\0就有很大的变化了

可以看到流提取是不受\0影响的,所以我们需要注意这个点,字符的打印在流提取不受\0影响 

为什么流提取的实现需要以ostream&作为返回值

(1)允许多次连续提取

(2)避免流对象的开销,规定直接传引用

strcpy与memcpy的区别

特性strcpymemcpy
参数类型char*void*
终止条件遇到\0停止按指定字节数完成复制
长度控制自动计算显式指定
数据安全高风险可控风险
适用场景纯字符串操作任意内存数据复制

所以对于字符串我们需要根据形式区分二者的拷贝,否则会出很大的问题  

流插入

在模拟流插入时我们同样要注意this指针的问题,因此需要在成员函数外面定义

我们看下面的问题:

注意(1):所以我们需要在输入之前清理之前原本的字符,然后重置_size,效果如下: 

 注意(2):我们每次调用+=,都会开辟空间,效率可以优化,先存进数组里面,再最后统一拷                         贝 

istream& operator>>(istream& in, string& _stl)
{//清理缓冲区_stl.clear();//输入元素char c = in.get();//临时数组int i = 0;char buff[128] = "\0";//直到c结束while (c != '\n' && c != '\0'){//先存入buff数组buff[i++] = c;//如果临时数组满了,就给_stlif (i == 127){memcpy(_stl._allocator, buff, i);//重置数组i = 0;}//_stl += c;//注意get会自动向后移动c = in.get();}//如果i没有重置,说明没有发生存满if (i != 127){for (int j = 0; j < i; j++){//如果满了就扩容if (_stl._size == _stl._capacity){_stl.reserve(2 * _stl._size);_stl._capacity = 2 * _stl._size;}//转移到对象里面_stl._allocator[_stl._size++] = buff[j];}}return in;
}

效果展示:

大小比较

我们拿 > 举例:大小比较我们一般采用的是运算符重载,里面根据当前字符的ASCII值比较

//大小比较
bool operator>(const string& S)const
{size_t p1 = 0, p2 = 0;//比较不同长度while (p1<_size && p2<S._size){if (_allocator[p1] == S._allocator[p2]){p1++;p2++;}else{if (_allocator[p1] > S._allocator[p2]){return true;}elsereturn false;}}//此时前面的字符都相等,但是没有比较完if (p1 < _size){return true;}elsereturn false;return false;
}

效果展示:

拷贝构造

原理我就不说了,咱们直接实现:

//拷贝构造
string(const string& S)
{_allocator = new char[S._capacity];_size = S._size;_capacity = S._capacity;//数据拷贝memcpy(_allocator, S._allocator, S._capacity);
}
赋值运算符重载

将一个对象的内容赋给另一个对象,因为前面我们已经有了一定的了解,我们下面换一种玩法:

注意:如果没有写拷贝构造等,属于浅拷贝,那么多次释放同一个空间会出问题

我们知道swap可以交换任意形式的变量,所以我们先来实现一个可以交换对象的swap:

思路:先根据赋值对象A拷贝构造一个临时对象B,然后把临时对象B的数据给*this

void swap(const string& S)
{//先开辟空间,注意S出了swap函数会销毁string tmp(S);//目的:创建一个临时变量,把临时变量的空间、大小等信息转给Sstd:: swap(_allocator, tmp._allocator);std:: swap(_size, tmp._size);std:: swap(_capacity, tmp._capacity);
}

下面我们直接调用这个swap函数就OK了:

string& operator=(string& S)
{(*this).swap(S);return *this;
}

效果展示:

最后我们再梳理以下思路:

现在有两个对象:A和B,我们的目标是A=B

进入swap函数先以B为模板调用拷贝构造出C,此时C是临时对象,内容与B完全一致

然后将C的内容交换给A,C虽然会释放,但是它在堆上开的内容只会main函数调用析构才会释放

 

这里我们先完成构造、析构,由于排版问题,下一篇我们来完成它的功能结尾!

                                                【雾非雾】期待与你的下次相遇! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/81599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【模型显著性分析】配对样本 t 检验

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言 t t t 检验配对样本 t t t 检验&#xff08;适用于相关组&#xff09;代码论文描…

商旅平台排名:十大商旅服务平台解析

商旅平台排名&#xff1a;十大商旅服务平台解析 在企业降本增效的关键转型期&#xff0c;商旅管理正成为优化运营成本与提升管理效能的核心场景。如何在保障出行体验的同时实现差旅成本精细化管控、管理流程智能化&#xff0c;成为越来越多企业的战略焦点。随着AI技术在数据洞…

题海拾贝:P1208 [USACO1.3] 混合牛奶 Mixing Milk

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…

每天掌握一个Linux命令 - ab(Apache Benchmark)

Linux 命令工具 ab 使用指南 一、工具概述 ab&#xff08;Apache Benchmark&#xff09; 是 Apache 官方提供的开源压力测试工具&#xff0c;用于衡量 Web 服务器的性能。它通过模拟多并发请求&#xff0c;测试服务器在高负载下的响应速度、吞吐量和稳定性&#xff0c;常用于…

AI的“空间盲症“

<------最重要的是订阅“鲁班模锤”------> 当我们看到一张照片时&#xff0c;大脑会自动分析其中的空间关系——哪个物体在前&#xff0c;哪个在后&#xff0c;左边是什么&#xff0c;右边是什么。但对于当今最先进的AI系统来说&#xff0c;这种看似简单的空间理解却是…

数据拟合实验

实验类型&#xff1a;●验证性实验 ○综合性实验 ○设计性实验 实验目的: 进一步熟练掌握最小二乘多项式拟合算法&#xff0c;提高编程能力和解决拟合问题的实践技能。 实验内容&#xff1a; 1 对下列数据&#xff0c;求解最小二乘抛物线f(x)Ax2BxC x -3 -1 1 3 y 15 5 …

系统思考:心智模式与业务创新

在最近的项目交付讨论中&#xff0c;我频繁听到一个词&#xff1a;“缺合适的人”。这让我陷入了深思&#xff1a;我们是否还在传统的生产力概念&#xff1f;纳瓦尔提出的三种杠杆&#xff1a;劳动力、资本、零边际成本产品。在当今这个信息化、全球化的商业世界中&#xff0c;…

python分步合并处理excel数据

文章目录 概要整体架构流程技术名词解释技术细节小结概要 客户需求 1. 背景与目标 用户需要将三个包含农业实验数据的Excel表格(AK、AN、AP)合并为一个结构化数据集,用于后续分析。每个表格包含相同类型的字段(如对照组与PSB处理组的样本数、均值、标准差),但需通过字…

Python爬虫实战:研究PyQuery库相关技术

1. 引言 1.1 研究背景与意义 随着互联网的快速发展,网络上的数据量呈爆炸式增长。如何高效地从海量的网页数据中提取有价值的信息,成为当前信息技术领域的一个重要研究方向。网络爬虫作为一种自动获取网页内容的程序,能够按照一定的规则,自动地抓取万维网信息,在搜索引擎…

深度学习---注意力机制(Attention Mechanism)

一、核心概念与发展背景 注意力机制是深度学习中模拟人类注意力选择能力的关键技术&#xff0c;旨在从海量信息中筛选关键特征&#xff0c;解决长序列信息处理中的瓶颈问题&#xff08;如RNN的梯度消失&#xff09;。其核心思想是&#xff1a;对输入序列的不同部分分配不同权重…

Jenkins分配对应项目权限与用户管理

在日常开发过程中经常会出现用户和权限管理问题&#xff0c;没有配置trigger时&#xff0c;通常需要我们手动构建&#xff0c;但此时前端和后端的朋友没有build权限&#xff0c;导致每次dev环境测试都需要麻烦我们手动去构建&#xff0c;消息传达不及时则会降低开发效率。 现有…

XCTF-web-file_include

解析 <?php highlight_file(__FILE__); // 高亮显示当前PHP文件源代码 include("./check.php"); // 包含检查文件&#xff08;可能包含安全过滤逻辑&#xff09;if(isset($_GET[filename])) { // 检查是否传入filename参数$filename $_GET[f…

matlab全息技术中的菲涅尔仿真成像

matlab全息技术中的菲涅尔仿真成像程序。 傅里叶法&#xff08;重建距离得大&#xff09;/Fresnel.m , 545 傅里叶法&#xff08;重建距离得大&#xff09;/FresnelB.m , 548 傅里叶法&#xff08;重建距离得大&#xff09;/Fresnel_solution.m , 1643 傅里叶法&#xff08;重…

CS144 - LAB0

CS144 - Lab 0 telnet 发送请求 如图&#xff0c;很简单&#xff0c;但是注意输入时间太久会超时 发邮箱 首先我们需要用命令行去发邮箱&#xff0c;这里我用企业微信邮箱给自己的 qq 邮箱发送~ 整个命令如下&#xff01; 对于其中的参数&#xff0c;其实从英文就可以看出来…

kafka SASL/PLAIN 认证及 ACL 权限控制

一、Zookeeper 配置 SASL/PLAIN 认证&#xff08;每个zookeeper节点都要做&#xff09; 1.1 在 zookeeper 的 conf 目录下&#xff0c;创建 zk_server_jaas.conf 文件&#xff0c;内容如下 Server {org.apache.kafka.common.security.plain.PlainLoginModule requiredusernam…

20250528-C#知识:函数简介及函数重载

C#知识&#xff1a;函数简介及函数重载 本文主要介绍函数参数和函数重载相关的知识点 1、函数 函数一般写在类中 一般函数调用 static int Add(int num, int value){num value;return num;}//一般函数调用&#xff0c;发生值类型参数的复制int num 1;Add(num, 1); //调用…

Vue内置指令与自定义指令

一、前言 在 Vue 开发中&#xff0c;指令&#xff08;Directives&#xff09; 是一种非常强大的特性&#xff0c;它允许我们以声明式的方式操作 DOM。Vue 提供了一些常用的内置指令&#xff0c;如 v-if、v-show、v-bind、v-on 等&#xff0c;同时也支持开发者根据需求创建自己…

华为AP6050DN无线接入点瘦模式转胖模式

引言 华为AP6050DN是一款企业级商用的无线接入点。由于产品定位原因,其默认工作在瘦模式下,即须经AC统一控制和管理,是不能直接充当普通的无线路由器来使用的。 而本文的目的,就是让其能脱离AC的统一控制和管理,当作普通无线路由器来使用。 硬件准备 华为AP6050DN无线接…

程序员出海之英语-使用手册

为什么现在实时翻译工具这么牛逼了&#xff0c;AI转译这么准确了&#xff0c;我还在这里跟老古董一样吭哧吭哧学英语呢&#xff1f; 这是因为我们始终是和人打交道&#xff0c;不仅仅是为了考试&#xff0c;看懂官方文章&#xff0c;听懂官方视频。这里为什么说官方&#xff0c…

Java 事务管理:在分布式系统中实现可靠的数据一致性

Java 事务管理&#xff1a;在分布式系统中实现可靠的数据一致性 在当今的软件开发领域&#xff0c;分布式系统逐渐成为主流架构。然而&#xff0c;这也给事务管理带来了巨大的挑战。本文将深入探讨 Java 事务管理在分布式系统中的关键要点&#xff0c;并通过详细代码实例展示如…