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

STL讲解——模拟实现string

STL讲解——模拟实现string

经典的string类问题

大厂在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的增、删、查、改、构造、拷贝构造、赋值运算符重载以及析构函数。大家看下自己可不可以写一个string类?

class string
{
public:string(const char* str = ""){// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下if(nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~string(){if(_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

大家肯定会想想我刚刚这样设计一个string类吧?可是你不觉得少了好多东西吗?

缺少什么呢:
1.缺少拷贝构造、赋值构造函数(虽然可以默认生成,但是都是浅拷贝
这种开辟空间的类肯定是不行的,析构函数会多次析构同一片区域)。
2.增删查改一个都没有。
3.析构函数需要自己编写。
4.iterator和re_iterator也没有编写(还有const形式的)。

仔细讲解一下为什么浅拷贝会引起报错:

在这里插入图片描述
就是说浅拷贝是:有个同学的抄你作业,把你的名字都给抄上了,这肯定有问题呀,一个班有两个你,老师一定要批评叫你家长呢!回到编译器方面,一个地址被释放一次变为空,可是还要再释放一次(该地址就变成了野指针了)释放野指针肯定会报错呀。(free(nullptr)是没问题的哦,但是释放野指针就会报错了

浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共
享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为
还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了深拷
贝。

深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情
况都是按照深拷贝方式提供。

在这里插入图片描述
也就是说我单独开一个空间,把我要复制的内容都复制过来,复制到了一个新空间(容器)中,等于这两个除了里面的内容一样,但是空间地址不一样了。

开始写正确的string类

先写一个命名空间,把自己设计的类放到你自己写的命名空间中,防止你有时候和std中的string冲突了。
以后自己写代码时最好不要把std库里面的东西都释放出来,自己写东西也设计一个命名空间。

传统版本:

namespace tom
{class string{public://构造函数string(const char* str=""):_size(strlen(str)),_capacity(_size){_str=new char[_capacity+ 1];strcpy(_str, str);}/*拷贝构造*/string(const string& s):_str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}//赋值构造string& operator=(const string& s){if (this != &s){char* Str = new char[strlen(s._str) + 1];if (Str){strcpy(Str, s._str);delete[] _str;_str = Str;_size = s._size;_capacity = s._capacity;}else{cout << "赋值失败" << endl;}}}~string(){if (_str){delete[] _str;_str = NULL;}}private:char* _str;size_t _capacity;size_t _size;};

这里的构造函数最好是把size和capacity在初始化列表中就初始化了,但是呢,
开空间还在构造函数中完成,不是说不能在初始化列表中完成,而是当你没有初始化string 是传一个‘\0’,
所以给一个缺省值“”,没错不写任何东西,默认里面只有一个‘\0’

析构函数
设计一个判断如果是空指针就不用处理了。

现代版本:

namespace tom
{class string{public:void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//构造函数string(const char* str=""):_size(strlen(str)),_capacity(_size){_str=new char[_capacity+ 1];strcpy(_str, str);}/*拷贝构造*//*string(const string& s):_str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}*/string(const string& s)//现代写法:_str(nullptr),_size(0),_capacity(0){string tmp(s._str);/*this->swap(tmp);*/swap(tmp);}//赋值构造string& operator=(string s){/*delete[] _str;_str = new char[strlen(s._str) + 1];strcpy(_str, s._str);*///现代写法swap(s);return *this;}private:char* _str;size_t _capacity;size_t _size;static const size_t Npos=-1;};

swap()
需要自己写一个string::swap()函数,为什么非要写一个类的swap呢,用函数库(algorithm.h)内的swap会有三次深拷贝,会降低效率。可是类内部的swap只用交换内置类型就可以了代价小很多。

然后利用传值拷贝形成临时拷贝变量,和this指针内的所有内容交换一下,由于是临时拷贝,出了作用域就会调用析构函数自动析构临时变量。太方便了!(要善于利用特性与机制)

增加关键细节

size()

设计一个string的size()函数 这个函数虽然很容易,但是相当重要。

//sizesize_t size()const{return _size;}

size()和lenth()是一样的所以就不写lenth()了。

[ ]方括号函数重载

就和字符串,数组的随机访问一样——arr[n] 或者str[n]
其实就是传元素的引用(因为是可以修改的,这时候是不是觉得引用的设计太棒了)

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

也一定要设计一个const类型

扩容

//扩容void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else {if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);_size = n;_str[n] = '\0';}}

reserve:就是普通的扩容,但是不能初始化。
resize:可以扩容,并且初始化你想要的字符。(还可以缩容)

迭代器

namespace tom
{class string{public:typedef char* iterator;typedef const char* const_iterator;typedef char* reverse_iterator;const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}reverse_iterator rbegin(){return _str + _size;}reverse_iterator rend(){return _str;}~string(){if (_str){delete[] _str;_str = NULL;}}private:char* _str;size_t _capacity;size_t _size;static const size_t Npos=-1;};
}

增、删、查、改

增加的设计

尾插
可以设计成一个尾插一个字母,在设计一个尾插一个字符串
于是乎设计一个push_back()尾插一个字母.
设计一个append()尾插一个字符串。
有了append()既可以设计一个运算符重载“+=”。

void push_pack(char ch){if (_size == _capacity){reserve(_capacity==0?4:_capacity * 2);//扩容一定要写这个判断,//因为刚开始是一个空字符串的话capacity=0,//乘二还是0.}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* str){int len = strlen(str);if (_size + len > _capacity){reserve(_size+len);}strcpy(_str + _size, str);_size += len;}string& operator+=(const char ch){push_pack(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}

这里的“+=”函数一定要重载一下(pushback()和append())。

删除

//删除string& erase(size_t pos=0 , size_t len=Npos){assert(pos < _size);if (len==Npos||pos+len>=_size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}

有了erase函数就可以函数服用设计一个pop_back()函数

void pop_back(){if (_size > 0){this->erase(_size - 1, 1);}}

查找

查找一个字符还是很简单的,循环判断就可以了。

		size_t find(char ch){for (size_t i = 0; i < _size; i++){if (ch == _str[i]){return i;}}return Npos; }

还可以设计一个查找字符串。再加一个小功能,指定位置开始查找,如果不给位置,再给个缺省值也行。(缺省值pos为0)

//查询整个字符串是否存在size_t find(const char* s,size_t pos=0){const char* ptr = strstr(_str + pos, s);if(ptr==nullptr){return Npos;}return ptr - _str;}

这个返回的位置就是ptr(肯定是大于等于_str)减去_str的值。.

更改

可以插入一个字符或者一个字符串。

//插入string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size+1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}string& insert(size_t pos, const char* s){assert(pos <= _size);size_t len = strlen(s);if (_size+len >= _capacity){reserve(_size+len);}size_t end = _size +len;while (end > pos){_str[end] = _str[end - len];--end;}//strcpy(_str + pos, s); 绝不能用,因为会把\0 也复制过去的。strncpy(_str + pos, s,len);_size+=len;return *this;}

有了这个插入函数就可以再设计一下push_back():

		void push_pack(char ch){insert(_size,ch);}void append(const char* str){insert(_size,str);}

这就简洁许多了,要学会复用。
其实学会服用,面试的时候真的可以在十分钟之内写完string类。

相关文章:

STL讲解——模拟实现string

STL讲解——模拟实现string 经典的string类问题 大厂在面试中&#xff0c;面试官总喜欢让学生自己来模拟实现string类&#xff0c;最主要是实现string类的增、删、查、改、构造、拷贝构造、赋值运算符重载以及析构函数。大家看下自己可不可以写一个string类&#xff1f; cla…...

CDH 6.3.2 升级Hive 2.3.9

升级背景 DolphinScheduler 3.1.1安装好后&#xff0c;其源码中集成的是Hive 2.1.1&#xff0c;版本太低&#xff0c;当在数据中心连接Hive数据源时报错&#xff0c;所以升级CDH自带的Hive为2.3.9版本。 一、准备工作 1、下载hive2.3.9并解压 下载地址&#xff1a;http://a…...

距离不是拦截我们前进的主因,与社科院杜兰金融硕士一起奔赴山海

最近有咨询社科院杜兰金融管理硕士项目的同学反馈他在西安&#xff0c;读研来北京上课太远了。一直在纠结要不要申请&#xff0c;其实距离不是问题&#xff0c;相向而行才是关键。在项目就读的同学好多也是来自外地&#xff0c;他们克服了种种困难来到项目学习&#xff0c;就是…...

【SpringBoot】MyBatis-plus 报错 sqlSessionFactory sqlSessionTemplate 最新解决办法

本文针对 MyBatis-plus&#xff0c;对于 MyBatis 报相同的错误&#xff0c;可以看这个大佬的文章&#xff1a;SpringBoot3整合MyBatis报错&#xff1a;Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required 针对报错如下&#xff1a; Property sqlSessionF…...

jsp诊疗预约系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp诊疗预约系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使用jav…...

详解 APISIX Lua 动态调试插件 inspect

作者罗锦华&#xff0c;API7.ai 技术专家/技术工程师&#xff0c;开源项目 pgcat&#xff0c;lua-resty-ffi&#xff0c;lua-resty-inspect 的作者。 原文链接 为什么需要 Lua 动态调试插件&#xff1f; Apache APISIX 有很多 Lua 代码&#xff0c;如何在运行时不触碰源代码的…...

#科研筑基# python初学自用笔记 第五篇 函数

调用函数python有很多内置函数&#xff0c;我们可以直接调用&#xff0c;详见python官方文档&#xff1a;内置函数 — Python 3.11.2 文档&#xff0c;也可以在命令行中输入help(函数名)来查看该函数的使用法则。函数名的本质就是指向一个函数对象的引用&#xff0c;完全可以用…...

设计模式之策略模式

一.基本内容1 . 实例有各种鸭子&#xff08;野鸭&#xff0c;北京鸭子&#xff0c;水鸭等&#xff0c;鸭子有各种行为&#xff0c;比如飞&#xff0c;叫等显示鸭子的信息传统方法解决&#xff1a;鸭子为抽象类&#xff0c;具体鸭子继承抽象类2.传统方法的不足&#xff1a;其他鸭…...

dbdeployer 使用札记

https://github.com/datacharmer/dbdeployer默认配置文件为当前用户的$HOME/.dbdeployer/config.json作为配置文件&#xff0c;可以通过dbdeplyoer defaults export导出并修改配置或者直接通过dbdeployer defaults update来更新默认文件&#xff0c;配置文件包含MySQL初始信息。…...

MATLAB算法实战应用案例精讲-【图像处理】数字图像模糊化(附Java、python和matlab代码实现)

目录 前言 几个相关概念 噪声 滤波器 算法原理 算法思想 噪...

搭建Hexo博客-第1章-Git和GitHub以及Coding的简单用法

搭建Hexo博客-第1章-Git和GitHub以及Coding的简单用法 搭建Hexo博客-第1章-Git和GitHub以及Coding的简单用法 Coding GitHub Hexo Markdown 搭建博客 大家好&#xff0c;这是我第一次写博客。使用 GitHub Hexo 创建最基本的博客很容易&#xff0c;网上有很多现成的教程。…...

【C++修行之路】C/C++内存管理

文章目录程序区域内存划分C语言动态内存分配&#xff1a;new和delete&#xff1a;new、delete和malloc、free的区别:程序区域内存划分 C/C程序内存区域划分非常相似。 C语言动态内存分配&#xff1a; malloc、calloc、realloc都是C语言动态开辟内存的常用函数 其中 malloc 开…...

spring cloud alibaba Sentinel(四)

服务雪崩 在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。 如果一个服务出现了问题&#xff0c;调用这个服务就会出现线程阻塞的情况&#xff0c; 此时若有大量的请求涌入&#xff0c;就会出现多条 线程阻塞等待&#xff0c;进而导致服务瘫痪。 由于服…...

Redis第三讲

目录 三、Redis03 3.1 Redis持久化之RDB 3.1.1 什么是RDB 3.1.2 备份是如何执行的 3.1.3 Fork 3.1.4 RDB持久化流程 3.1.5 dump.rdb文件 3.1.6 配置rdb文件生成位置 3.1.7 如何触发RDB快照以及保持策略 3.2 Redis持久化之AOF 3.2.1 什么是AOF 3.2.2 AOF持久化流程 …...

JAVA线程池的使用

一、池化思想和JAVA线程池 池化是很重要的思想&#xff1b;池化的好处是提供缓冲和统一的管理。这个笔者在本人的数据库连接池的博客中已经提到过了&#xff08;JAVA常用数据库连接池_王者之路001的博客-CSDN博客 &#xff09;。 线程池是另一种池化思想的运用&#xff0c;把…...

力扣56.合并区间

文章目录力扣56.合并区间题目描述排序合并力扣56.合并区间 题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中…...

代码随想录二刷Day03链表: 24.两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表||

24.两两交换链表中的节点 文章链接&#xff1a;代码随想录 (programmercarl.com) 思路&#xff1a; &#xff08;1&#xff09;首先如果要处理相邻两个节点的话&#xff0c;一定需要操作两个节点的前一个节点才可以&#xff0c;因此&#xff0c;本题需要设定一个虚拟头节点 …...

我应该在我的博客上写什么? 介绍如何撰写初学者容易担心的文章

我想有很多人开了博客&#xff0c;但想不起来写作&#xff0c;无法取得进展。 博客的主题和文章的内容不会仅仅通过写你想做的事情来工作。 重要的是要了解用户想要阅读的内容以及人们可能收集的内容&#xff0c;并将其与您想要编写的内容很好地匹配。 这一次&#xff0c;我…...

嵌入式C语言设计模式 --- 外观模式

1 - 什么是外观模式? 外观模式(Facade Pattern),是一种比较简单的结构型模式,它存在的目的,也是为了简单。 外观模式隐藏了一系列接口的复杂性,旨在为外部客户端提供一个更高层次且统一简单的接口,简化了客户端调用某些模块的一系列操作。 外观模式应该是软件工程师…...

若依ruoyi——手把手教你制作自己的管理系统【三、代码生成】

昨天情人节一(&#xffe3;︶&#xffe3;*)) 送给赛利亚一((*&#xffe3;3&#xffe3;)╭ ********* 专栏略长 爆肝万字 细节狂魔 请准备好一键三连 ********* 修改后的页面&#xff1a; 干干净净贼舒服一Ψ(&#xffe3;∀&#xffe3;)Ψ——Ψ(&#xffe3;∀&#x…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...