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

C++笔记-string(下)

这篇我们自己来简单实现一下string类中的各个接口,来帮助我们更好地理解string类接口的底层原理。

1.构造函数和析构函数

对于构造函数我们要写两种情况:空字符串和非空字符串

因为我们要自己实现string类,所以就不能用std命名空间,这里我们要自己创建一个命名空间,在里面实现string类。

我们之前也讲过string类底层就是一个数组,所以我们在实现时也按数组来实现,并定义数组中数据的大小和容量,就和我们实现顺序表一样。

这里面我先实现了c_str接口,因为我们现在是自定义类型,所以不能直接输出变量,所以实现c_str接口便于我们输出我们实现的字符串。

我们首先讲空字符串的情况:
这里可能有人会疑惑我为什么这样初始化?

可能觉得既然是空字符串,直接定义为null不就好了。我想大多数人的第一想法也是这样,但其实这样写是有问题的。

当我们初始化一个空字符串时,我们调用c_str接口时,返回值是char*类型,而此时字符串是空指针,乍一看没问题,感觉那就输出空指针嘛,没设么没问题啊。

但是char*这个类型比较特殊,它默认是当字符串输出的,所以它会解引用,此时就出问题了,对空指针解引用是很危险的事,所以不能初始化为null。

而字符串在底层保存时,其实会多开辟一块空间,来存储'\0',所以我们创建一个空间来存放'\0',_size和_capacity初始化为0即可。

下面讲非空字符串:
给一个默认空字符串也是为了调用但不传参数这种情况,而""这种在底层就是'\0'。

而是为了模拟真实的string类,所以我们创建空间时多创建一个空间来存放'\0'。

再把传入的参数拷贝到我们创建的空间即可。

最后是析构函数:
析构函数就和之前讲的一样,new出来的空间delete就行,再把_str置为null即可。

2.遍历字符串

在遍历字符串中我们要实现的就是size接口,[]符号重载以及迭代器的实现。

首先讲size:
这个就很简单,直接返回_size即可。

下面讲[]符号重载:
[]符号重载我们要实现两种,const类型和非const类型。

实现起来也简单,直接返回是相应位置的值即可。const类型和非const类型区别就是能否通过解引用来改变字符串的内容。

最后是迭代器的实现:

迭代器我们就只实现begin和end,并且每种都实现const类型和非const类型。

迭代器不一定是指针,但在这里实现时我们就用指针来实现,毕竟和指针的作用一样。

实现起来也简单,begin就是返回第一个元素的地址,end就是返回最后一个数据的下一个位置的地址。

3.修改字符串

修改字符串要实现的比较多,我就一个一个讲:
3.1reserve

reserve虽然能扩容也能缩容,但是缩容是分编译器的,并且实现起来较为麻烦,涉及当前未学的知识,所以这里我只实现扩容功能。

1.创建一个临时变量来接收我们扩容后的空间

2.讲当前数组的内容拷贝到新空间中

3.讲究空间销毁,并将_str指向新空间

4._capacity记录新的容量

3.2push_back

push_back就是尾插,注意的点就是要首先判断数组是否满了,满了就要扩容,最后在原_size的位置处插入数据,_size++,再将新_size位置置为'\0'。

3.3append

实现append时要先计算字符串的长度,并判断我们之前的扩容方式扩容后与_size+len谁更大,取更大的作为我们的扩容方式。

接下来就是把字符串拷贝进数组,这里我用memcpy进行拷贝,避免字符串中中间就含有'\0'导致拷贝终止。

注意:拷贝时要拷贝len+1个字符,所以最后一步可写可不写,或者拷贝len个字符,这样就要写上最后一步。

3.4+=运算符重载

实现了push_back和append后,实现+=运算符重载就简单了很多,唯一需要注意的就是我们要返回的是字符串本身,所以要注意返回值是string。

3.5insert

3.5.1插入一个字符

1.assert判断pos是否在有效范围内

2.判断是否需要扩容

3.因为底层数组,所以在中间位置插入一个数据就要将pos位置以后的数据全部向后移一位

4.将pos位置处的值置为要插入的数据

5._size++

至于为什么要把pos强转成int,这是因为我们这个循环的结束条件是i<pos,如果是其他位置当然没问题,但如果是头插那就有问题了。

当时头插时,i只有等于-1时循环才会停止,但是当i等于-1时会出现类型提升的现象,类型提升就是范围小的向范围大的提升,有符号的向无符号的提升,此时i就会向无符号整型提升,-1转为无符号整型是整形最大值,所以这个程序会死循环导致崩溃。

将参数中的pos改为int也能解决问题,但是在底层编译器在实现时用的都是无符号,所以为了保持一致,这里也就用无符号。

3.5.2插入一个字符串

1.assert判断pos是否在有效范围内

2.计算字符串的长度

2.判断是否需要扩容(这里扩容是和append一样的原则)

3.将pos位置以后的值全部向后移动len个位置

4.将字符串一个一个插入数组中,注意字符串的下标是i-pos

5._size+len

3.6erase

这里说明一下,在vs编译器下被const修饰的静态的整型变量是可以在类内定义的,也就是给缺省值,这是一种特殊情况。

因为在底层如果不传参数len,其实有个默认值就是npos,这个值其实是整型的最大值,但在定义时是-1。

1.assert判断pos是否在有效范围内

2.判断是删完还是只删一部分

3.只删一部分直接把pos位置处的值置为'\0',再将_size移到pos处,这样也算完成了删除

4.只删一部分,这里我用了memmove的方式将pos位置后面的值移到以pos为起始的位置处,其实和后面数据全部向前移len个位置是一样的,当然也可以用循环来实现。

5._size-=len

3.7find

3.7.1查找单个字符

查找单个字符相对简单,遍历整个字符串,找到对应字符就返回下标,没有找到就返回-1。

注意:在参数中有个pos,这个参数给个缺省值0,代表如果不传参数,就默认从下标为0的位置开始查找。

3.7.2查找一个字符串

查找一个字符串这里我使用strstr,这里用strstr来实现会更方便,如果查找成功strstr返回的是首个字符的地址,查找失败返回null。

这里如果查找失败就返回-1,查找成功就返回首个字符的下标。

注意:因为查找成功返回的是首个字符的地址,所以要想返回下标,就需要指针相减来得到。

3.8substr

substr作用就是得到某个区间的字符,所以参数也要给上缺省值。

1.定义空字符串m

2.判断len是否超出范围,超出范围按最大值算

3.提前扩容,提高效率

4.通过循环,将区间上的字符拷贝到m

5.返回m

3.9=符号重载

1.new一个临时空间,注意空间大小是_capacity+1,最后一个位置用来存放'\0'

2.调用memcpy函数将str中的数据拷贝过去

3.delete掉原先的_str,让_str指向新的空间tmp

4.返回*this

3.10<<符号重载

这是我们平常输出所用的符号,这个符号属于ostream流中的,所以参数我们也要用ostream类型。

这里解释一下为什么是两个参数:

这是我们输出时写的形式,可以看出在符号左边是ostream流中定义的变量cout,而参数右边是我们要输出的字符串,所以在实现<<时参数是这两个的原因,并且第一个参数要用引用,道理是和传值调用是一样的,这里就不过多赘述了。

而输出也很简单,遍历整个数组挨个将字符输出即可。

注意:在声明时可以不用把这个重载放在类中,如果要放在类中需要用到友元,这里我是在类外声明和定义的,所以不需要友元声明,并且我在ostream前加上std::是当前类中并没有展开std标准库,而ostream是在标准库中的,下面的<<符号重载也是如此。

3.11clear

clear因为下面>>符号重载需要用到,所以提前写一下。

clear就是将当前字符串清空,所以实现起来也相对简单:

直接将数组第一个位置赋值为'\0',再将_size和_capacity置为0即可。

3.12>>符号重载

1.清空字符串

2.利用get函数得到输入的每一字符

3.利用循环使str加上每一个输入的字符

注意:1.参数中的str此时不需要加const,因为要对str进行修改

2.解释一下这里为什么这里不用>>符号:
因为>>符号会自动掠过空格和换行符号,而我们输入字符的停止条件就是遇到空格或者换行字符,所以这里不能用>>符号,否则会一直输入不会停止

4.判断字符串

判断字符串就是判断字符串之间的大小情况,而这里实现<和==符号即可,剩下的符号用这两个即可实现。

4.1<符号

1.len1和len2来记录两个字符串的大小

2.i1和i2记录下标

3.以其中一个结束为循环条件,判断大小

4.最后返回的这个表达式是当两个字符串长度不相等时,前面都相等,最后跳出循环判断是true只有表达式这一种情况,其他都是false,所以直接返回这个表达式的结果即可

注意:循环条件是<而不是<=。

4.2==符号实现

前面和<符号实现基本相同,在循环中判断条件作出修改,如果相应位置字符不相等直接返回false。

跳出循环后,只有表达式这一种情况是true,其余情况都为false,所以直接返回表达式即可。

4.3<=符号实现

4.4>符号实现

4.5>=符号实现

4.6!=符号实现

实现了<和==符号后,剩下的符号实现起来就很简单。

这里可能有人会有疑惑:为社么不用strcmp或者memcpy来实现呢?

先解释strcmp:

一般情况下确实没什么问题,但我们要清楚strcmp遇到'\0'会停止,如果字符串中间含有'\0'呢?此时就会出问题,比较就会停止,所以不能用。

memcpy:
memcpy虽然解决了strcmp的问题,但是它面临两个字符串长度不一,到底是按大的走还是按小的走呢?不管按那个走都会面临越界访问的问题,所以不能用。

到最后还是我们得自己来实现。

以上就是string(下)的内容。

相关文章:

  • C++类模板的运用
  • spring功能汇总
  • 动态规划-杨辉三角
  • Python字典实战: 三大管理系统开发指南(班级+会议+购物车)(附源码)
  • git修改已经push的commit的message
  • Spring Boot中Spring MVC相关配置的详细描述及表格总结
  • C4D XP 粒子动画云端渲染指南
  • 控制理论-传递函数
  • 【docker】
  • 前后端 Mock 技术实践指南【大模型总结】
  • 1、window 下SDL 下载使用, 测试环境搭建
  • 位运算题目:N 天后的牢房
  • [数据集]The Natural Scenes Dataset (NSD)介绍,申请及使用方法
  • 【蓝桥杯】算法笔记6
  • CubeMX配置STM32VET6实现网口通信(无操作系统版-附源码)
  • win11 nacos2.2.1 报错curl -X post 报错找不到和名称X匹配参数;不是命令
  • excel经验
  • (三十七)Dart 中使用 Pub 包管理系统与 HTTP 请求教程
  • leetcode0410. 分割数组的最大值-hard
  • 程序化广告行业(69/89):DMP与PCP系统核心功能剖析
  • 日本做翻译的网站/营销推广投放平台
  • 做外贸网站企业/南宁seo服务优化
  • 九江便宜做网站/培训心得总结
  • 天津网站推广公司哪家好/网络推广渠道公司
  • 常州网站建设/推广合作
  • 温州网站建设方案报价/百度竞价推广点击软件奔奔