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

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

距离不是拦截我们前进的主因,与社科院杜兰金融硕士一起奔赴山海
最近有咨询社科院杜兰金融管理硕士项目的同学反馈他在西安,读研来北京上课太远了。一直在纠结要不要申请,其实距离不是问题,相向而行才是关键。在项目就读的同学好多也是来自外地,他们克服了种种困难来到项目学习,就是…...
【SpringBoot】MyBatis-plus 报错 sqlSessionFactory sqlSessionTemplate 最新解决办法
本文针对 MyBatis-plus,对于 MyBatis 报相同的错误,可以看这个大佬的文章:SpringBoot3整合MyBatis报错:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required 针对报错如下: Property sqlSessionF…...

jsp诊疗预约系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
一、源码特点 jsp诊疗预约系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用jav…...
详解 APISIX Lua 动态调试插件 inspect
作者罗锦华,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。 原文链接 为什么需要 Lua 动态调试插件? Apache APISIX 有很多 Lua 代码,如何在运行时不触碰源代码的…...
#科研筑基# python初学自用笔记 第五篇 函数
调用函数python有很多内置函数,我们可以直接调用,详见python官方文档:内置函数 — Python 3.11.2 文档,也可以在命令行中输入help(函数名)来查看该函数的使用法则。函数名的本质就是指向一个函数对象的引用,完全可以用…...
设计模式之策略模式
一.基本内容1 . 实例有各种鸭子(野鸭,北京鸭子,水鸭等,鸭子有各种行为,比如飞,叫等显示鸭子的信息传统方法解决:鸭子为抽象类,具体鸭子继承抽象类2.传统方法的不足:其他鸭…...
dbdeployer 使用札记
https://github.com/datacharmer/dbdeployer默认配置文件为当前用户的$HOME/.dbdeployer/config.json作为配置文件,可以通过dbdeplyoer defaults export导出并修改配置或者直接通过dbdeployer defaults update来更新默认文件,配置文件包含MySQL初始信息。…...
MATLAB算法实战应用案例精讲-【图像处理】数字图像模糊化(附Java、python和matlab代码实现)
目录 前言 几个相关概念 噪声 滤波器 算法原理 算法思想 噪...

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

【C++修行之路】C/C++内存管理
文章目录程序区域内存划分C语言动态内存分配:new和delete:new、delete和malloc、free的区别:程序区域内存划分 C/C程序内存区域划分非常相似。 C语言动态内存分配: malloc、calloc、realloc都是C语言动态开辟内存的常用函数 其中 malloc 开…...

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

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线程池 池化是很重要的思想;池化的好处是提供缓冲和统一的管理。这个笔者在本人的数据库连接池的博客中已经提到过了(JAVA常用数据库连接池_王者之路001的博客-CSDN博客 )。 线程池是另一种池化思想的运用,把…...

力扣56.合并区间
文章目录力扣56.合并区间题目描述排序合并力扣56.合并区间 题目描述 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中…...
代码随想录二刷Day03链表: 24.两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表||
24.两两交换链表中的节点 文章链接:代码随想录 (programmercarl.com) 思路: (1)首先如果要处理相邻两个节点的话,一定需要操作两个节点的前一个节点才可以,因此,本题需要设定一个虚拟头节点 …...

我应该在我的博客上写什么? 介绍如何撰写初学者容易担心的文章
我想有很多人开了博客,但想不起来写作,无法取得进展。 博客的主题和文章的内容不会仅仅通过写你想做的事情来工作。 重要的是要了解用户想要阅读的内容以及人们可能收集的内容,并将其与您想要编写的内容很好地匹配。 这一次,我…...

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

若依ruoyi——手把手教你制作自己的管理系统【三、代码生成】
昨天情人节一( ̄︶ ̄*)) 送给赛利亚一((* ̄3 ̄)╭ ********* 专栏略长 爆肝万字 细节狂魔 请准备好一键三连 ********* 修改后的页面: 干干净净贼舒服一Ψ( ̄∀ ̄)Ψ——Ψ( ̄∀&#x…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...