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…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
