c++学习之string实现

字符串 - C++引用 (cplusplus.com)这里给出标准官方的string实现,可以看到设计还是较为复杂的,有成员函数,迭代器,修饰符,容量,元素访问,字符串操作等,将字符尽可能的需求都设计出来,我们这里实现string中比较常用且重要的。

成员变量

private:char* _str;size_t _size;size_t _capacity;
public:const static size_t npos;//声明一个npos 表示返回找不到的下标 或从-1的下一个开始寻找

目录

1.迭代器实现

2.成员函数

空构造

 有参构造

 拷贝构造

析构

 重载=

3.字符串操作

c_str

 find 

 substr

5.修饰符

push_back

 append

 operator+=

insert

 erase

6.容量

reserve

size

capacity

resize 

7.元素访问

重载下标引用操作符

8.运算符重载

重载<

重载==

重载<=

重载>

重载>=

重载!=

重载输入流

重载输出流


1.迭代器实现

这里我们实现了常量迭代器与正常的迭代器,反向迭代器并未实现。对于string这里的迭代器我们可以看到本质上是一个char *类型的指针,在某些地方可能不是指针,且它的用法也是与指针如出一撤。这里对于反向迭代器后面再实现。

    typedef char* iterator;//迭代器const typedef char* const_iterator;//常量迭代器iterator begin(){return _str;};iterator end(){return _str+_size;};const_iterator begin()const{return _str;};const_iterator end()const{return _str + _size;};

在这里, 我们将char*定义为一个新的类型,并且给出它的成员函数,也就是迭代器的各个位置,从而实现。利用迭代器我们可以对他进行遍历:

Mystring::iterator it = str1.begin();while (it != str1.end()){cout << *it ;++it;}//范围forfor (auto tmp : str1){cout << tmp ;}

 同时其实本质上范围for的底层就是利用迭代器实现的,编译器在遇到范围for这样的写法时就会将这一段代码替换成下面迭代器实现的这样的遍历。

2.成员函数

空构造

实际上并不需要实现,我们这里给出它的实现,是了解对于空字符串的构造函数,虽说是空字符串,但不可直接赋值nullptr,否则后面实例化对象调成员函数时就是解引用空指针,空字符串,故这里会报错,实际上实现会给几个字节空间,放\0

Mystring():_str(new char[1]),_capacity(0),_size(0){}

 有参构造

//有参构造Mystring(const char* str="") : _size(strlen(str)), _capacity(_size)//缺省值直接给"",不给"\0",原先就有\0{//注意这里的字符串利用深拷贝,且初始化顺序遵循声明顺序_str = new char[_capacity + 1];//多开一个给\0strcpy(_str, str);}

 拷贝构造

还是老问题,直接的赋值会造成两次析构,故这里需要进行深拷贝。

//拷贝构造//之前讲过,浅拷贝会析构两次,深拷贝解决Mystring(const Mystring& s){_str = new char[s._capacity + 1];memset(_str, 0, s._capacity);strcpy(_str, s._str);this->_size = s._size;this->_capacity = s._capacity;}

析构

释放及初始化

//析构~Mystring(){delete [] _str;_str = nullptr;_capacity = _size = 0;}

 重载=

// 赋值重载Mystring& operator=(const Mystring& s){if (*this != s){char* temp = new char[s._capacity+1];strcpy(temp, s._str);delete []_str;_str = temp;this->_size = s._size;this->_capacity = s._capacity;}return *this;}

 这里的赋值重载区分拷贝构造:

用现有对象初始化定义的对象---------拷贝构造

用现有对象赋值给现有对象------------赋值

3.字符串操作

这里实现几个比较重要的,常用的。

c_str

const char* c_str()const{return _str;}

 find 

这里给出了两种实现,字符寻找与字符串寻找

//寻找某个字符,返回其下标位置size_t find(char ch,size_t pos=0){for (size_t i = pos; i < _size; i++){if (ch == _str[i]){return i;}}return npos;//找不到,返回-1}//寻找某个字符串,返回其相同的个数size_t find(const char* ch, size_t pos = 0){//暴力匹配const char* tmp = strstr(_str+pos, ch);if (tmp!= nullptr){return tmp - _str;//指针相减,找到个数}else{return npos;}}

 substr

//从pos位置处取n个字符,或n到 nposMystring substr(size_t pos, size_t len=npos){assert(pos < _size);Mystring s;size_t end = pos + len;if (len == pos || len + pos >= _size){len = _size - len;end = _size;}s.reserve(len);for (size_t i = pos; i <pos+len; i++){s += _str[i];//这里利用重载后的+=,本质上就是尾插}return s;}

5.修饰符

push_back

//尾插字符void push_back(char ch){//尾插的操作很简单,但是要考虑到扩容的问题if (_size == _capacity){reserve(_capacity=0?4:_capacity * 2);//为了避免空间是0,扩容失败}_str[_size] = ch;++_size;_str[_size] = '\0';}

 append

//尾插字符串void append(const char*ch){size_t num = strlen(ch);if (num + _size > _capacity){reserve(num + _size);}strcpy(_str + _size, ch);_size += num;//注意这里直接运用strcpy拷贝过来,连同\0一起,之后变为新大小}

 operator+=

这里重载的+=是利用了尾插的实现,故也作为字符串修饰

也是两种实现,+=字符或字符串

Mystring& operator+=(const char ch){push_back(ch);return *this;}Mystring& operator+=(const char* ch){append(ch);return *this;}

insert

 也是有两种实现方式

注意注释的提示:若这里想要进行头插,此时end>=0。只有当end--到负数才停止,而size_t 无法去判断正负数,程序崩溃,解决方法,将类型转换为int ,或者修改后移顺序,_str[end] = _str[end-1];去掉条件=。

其次就是对于扩容需要考虑空间为0的情况。

//某个位置插入字符void insert(size_t pos,char ch){assert(pos < _size);if (_size == _capacity){reserve(_capacity = 0 ? 4 : _capacity * 2);//为了避免空间是0,扩容失败}int end = _size;//pos位置处后移while (end>=(int)pos){//后移_str[end + 1] = _str[end];--end;}//插入_str[pos] = ch;}//某个位置插入字符串void insert(size_t pos,char *ch){size_t len = strlen(ch);if (_size +len>_capacity){reserve(_capacity = 0 ? 4 : _capacity * 2);//为了避免空间是0,扩容失败}size_t end = _size;//pos位置处后移while (end >= pos){//后移_str[end + len] = _str[end];--end;}//插入strncpy(_str+pos, ch,len);_size += len;}

 erase

删除pos位置后的npos个字符,这里的npos给的是缺省值,再没参数的情况下为-1,也就是pos后的全部删除。

//删除void erase(size_t pos,size_t len= npos){assert(pos <= _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin- len] = _str[begin ];++begin;}_size -= len;}}

6.容量

reserve

//扩容void reserve(size_t n){if (n > _size){char* tmp = new char[n + 1];//这里还是留一个\0位置strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}

size

//返回大小
size_t size()const{return _size;}

capacity

//返回容量
size_t capacity()const{return _capacity;}

resize 

//初始化大小为n,且可以赋值之后的void resize(size_t n,char c='\0'){if (n <= _size){//删除_str[n] = '\0';_size = n;}else{//扩容reserve(n);//初始化值while (_size < n){_str[_size] = c;_size++;}//末尾给\0_str[_size] = '\0';}}

7.元素访问

重载下标引用操作符

const char& operator[](int pos)const{assert(pos < _size);return _str[pos];}

8.运算符重载

对于这里的比较运算符,通过实现<与==,再取反实现> ,>=,!=.

重载<

bool operator<(const Mystring &s1){return strcmp(this->_str, s1._str) <0;}

重载==

bool operator==(const Mystring& s){return strcmp(this->_str, s._str) == 0;}

重载<=

bool operator<=(const Mystring& s){return *this < s || *this == s;//由于<与=已实现,直接用}

重载>

bool operator>(const Mystring& s){return !( *this <=s) ;//取反}

重载>=

bool operator>=(const Mystring& s){return !(*this < s);//取反}

重载!=

bool operator!=(const Mystring& s){return!(*this==s);}

重载输入流

//流插入
istream& operator>>(istream& in, Mystring& s)
{char ch;ch = in.get();//一个个拿字符,包括空格与\nwhile (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;
}

重载输出流

//流提取
ostream& operator<<(ostream& out, const Mystring& s)
{for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}

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

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

相关文章

Jmeter(三十):并发测试(设置集合点)

集合点:让所有请求在不满足条件的时候处于等待状态。 如:我集合点设置为50,那么不满足50个请求的时候,这些请求都会集合在一起,处于等待状态,当达到50的时候,就一起执行。从而达到并发的效果。 那么Jmeter中可以通过同步定时器 Synchronizing Timer 来完成。 Number …

计算机竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

医学影像PACS系统源码,患者登记、图像采集和处理、图像存储、报告产生的影像系统

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

高忆管理:新手炒股入门零基础学?

炒股是一些人为了取得高额回报和更好的财政自由而进行的活动。但对许多新手而言&#xff0c;这是一个全新的领域&#xff0c;需求掌握许多根底常识才能够开始加入炒股商场。本文将为零根底的新手炒股入门供给一些主张和技巧&#xff1a; 一、学习根底常识 关于炒股入门的新手而…

spark内置函数笔记

spark常用的内置函数 一、统计函数 1. approx_percentile selectapprox_percentile( id, array(0.5, 0.7, 0.9), 1000) as res from (select explode(sequence(1,1000)) as id) aa ; [499,699,899] 二、数组函数 1. array -- 构造数据组 select array(1, 2, 3); [1,2,3]-- 数…

在ros中利用串口serial发布fdilink的gps话题

文章目录 介绍FDILink通讯协议数据帧组成数据包 数据处理打开串口在头文件中定义参数串口读取 代码运用依赖&#xff1a;使用&#xff1a; 源码 介绍 DETA100系列 是一个提供 GNSS/INS & AHRS 系统的模组&#xff0c;在最苛刻的条件下提供准确的位置、速度、加速度和姿态数…

Java抽象类——抽象类与普通类的区别

abstract抽象类 1、含义&#xff1a;类和类之间具有共同特征&#xff0c;将这些共同特征提取出来&#xff0c;就形成抽象类。。。 在 java 中&#xff0c;被 abstract 关键字修饰的类叫做抽象类&#xff0c;被abstract关键字修饰的方法叫做抽象方法。 抽象类本身是不存在的&…

量化面试-概率题

文章目录 一、题目1.糖果罐&#xff08;绿皮书79页&#xff09;2 折木棍&#xff08;绿皮书89页&#xff09;3 第一张ACE&#xff08;绿皮书95页&#xff09;4 n个均匀分布之和&#xff08;绿皮书95页&#xff09; 二、答案1. 糖果罐2 折木棍3 第一张ACE4 n个均匀分布之和 一、…

spring事件和线程池区别

Spring事件&#xff08;Spring Event&#xff09;和线程池&#xff08;Thread Pool&#xff09;是两个不同的概念。 Spring事件是Spring框架中的一种机制&#xff0c;用于在应用程序中实现发布-订阅模式。通过定义事件和监听器&#xff0c;可以在特定事件发生时&#xff0c;通…

pytest笔记: pytest单元测试框架

第一步&#xff1a;安装 和查看版本 pycharm settings 查看 第二步&#xff1a; 编写test_example.py def inc(x):return x1 def test_answer():assert inc(4) 5 第三步&#xff1a;在当前路径下执行pytest 命令 PS E:\data\web测试\Selenium3自动化测试实战——基于Pyth…

专题:曲面的切平面、法线

假设曲面方程为隐函数 F ( x , y , z ) 0 &#xff0c;点 M ( x 0 , y 0 , z 0 ) 是其上一点 又在点 M 处任意引一条在曲面上的曲线&#xff0c;设该曲线参数方程为&#xff1a; { x φ ( t ) y ψ ( t ) z ω ( t ) &#xff0c;且当 t t 0 时&#xff0c; x x 0 , y y…

Flink流批一体计算(18):PyFlink DataStream API之计算和Sink

目录 1. 在上节数据流上执行转换操作&#xff0c;或者使用 sink 将数据写入外部系统。 2. File Sink File Sink Format Types Row-encoded Formats Bulk-encoded Formats 桶分配 滚动策略 3. 如何输出结果 Print 集合数据到客户端&#xff0c;execute_and_collect…

鼎盛合方案——充气泵的原理和结构

充气泵是一种常见的气体压缩设备&#xff0c;它可以将空气或其他气体压缩成高压气体&#xff0c;用于充气、输送、压缩等工业和生活领域。充气泵的原理是利用机械或电动力量将气体压缩&#xff0c;使其体积减小&#xff0c;压力增大&#xff0c;从而达到充气的目的。 充气泵的…

力扣:74. 搜索二维矩阵(Python3)

题目&#xff1a; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非递减顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返…

解决Linux报错:Swap file “xxxxxx.swp“ already exists

出现问题 Swap file “.models_conf.yaml.swp” already exists! 在 Linux 下 vim 编辑过程中&#xff0c;由于某种原因异常退出正在编辑的文件&#xff0c;再次编辑该文件时&#xff0c;会出现如下提示&#xff1a; 一个文件出现了带有.swp的副本文件的时候&#xff0c;会出现…

MySQL概述,架构原理

一.MySQL简介 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典的MySQL AB公司开发&#xff0c;后被oracle公司收购&#xff0c;MySQL是当下最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&#xff0c;MySQL是最好的RDBMS&#xff08;Relational Database Man…

Mybatis小记

目录 Mybatis第一个程序 xml文件 测试类 错误排查 Mybatis第一个程序 1.搭建实验数据库 2.导入MyBatis相关jar包 3.编写MyBatis核心配置文件 4.编写MyBatis工具类 5.创建实体类、 6.编写Mapper接口类 7.编写Mapper.xml配置文件 8.编写测试类 对象传参只引用需要的属性就可…

【设计模式】Head First 设计模式——装饰者模式 C++实现

设计模式最大的作用就是在变化和稳定中间寻找隔离点&#xff0c;然后分离它们&#xff0c;从而管理变化。将变化像小兔子一样关到笼子里&#xff0c;让它在笼子里随便跳&#xff0c;而不至于跳出来把你整个房间给污染掉。 设计思想 动态地将责任附加到对象上&#xff0c;若要扩…

新生代与老年代

在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;内存被划分为多个不同的区域&#xff0c;其中包括新生代&#xff08;Young Generation&#xff09;和老年代&#xff08;Old Generation&#xff09;。 新生代是用于存储新创建的对象的区域。大多数对象在创建后很快就变…

springboot RabbitMQ客户端连接故障恢复

最近做RabbitMQ故障演练发现RabbitMQ服务器停止后&#xff0c;基于springboot的消费端不可以自动的恢复&#xff0c;队列的消费者消失&#xff0c;消息一直积压到队列中&#xff0c;这种情况肯定是不可接收的&#xff1b;通过研究源代码找到了解决方案。 一、添加自动恢复配置a…