ifstream读取文件有什么要求(ifstream读取字符串)

ifstream读取文件有什么要求(ifstream读取字符串)在前文我们讲解了 string 类接口使用 C string 类的使用 上 C string 类的使用 下 本片文章就来模拟实现 string 类 注 本文实现的是 string 的部分重点内容 目的是为了更好的了解 string 而不是实现出一个功能一模一样的 string 负责 string 类函数的声明 一些短小且频繁调用的函数会在类里实现

大家好,我是讯享网,很高兴认识大家。



在前文我们讲解了string类接口使用(C++【string类的使用】(上),C++【string类的使用】(下)),本片文章就来模拟实现string类。
注:本文实现的是string的部分重点内容,目的是为了更好的了解string,而不是实现出一个功能一模一样的string

负责string类函数的声明(一些短小且频繁调用的函数会在类里实现)
在这里插入图片描述
讯享网

负责string类函数的实现
在这里插入图片描述

负责测试函数是否成功
在这里插入图片描述

 

讯享网
  • _str:是string类的关键,用来存放被开辟空间的地址
  • _size:非空字符的个数(string类的长度)
  • _capacity:string类当前的容量
  • _npos:静态成员函数,用来当缺省值

我们就实现两个构造:无参构造与字符串构造。

注意:无参构造不能给_str一个,因为string本质上还是读取字符串,而字符串只有遇到才会停止读取,如果你给了一个空指针,那么在查找_str的时候就查找不到,就会一直找下去,这时的程序就已经无法使用了。

不管是无参构造,还是带参构造,都要有,但是我们的并不包含,所以我们在开空间的时候是要多开一个空间的。

讯享网

我们也可以将这两个函数合二为一,给一个缺省值就好了。

 

注意:缺省值不需要给成,因为一个空字符串里面就默认包含了,如果我们在多给一个,里面就会存储两个,那就有点画蛇添足了。

我们看到_str是申请了空间,那么编译器默认生成的析构就无法满足我们的需求了,我们就需要自己手搓一个析构函数。

其实析构就很简单,就是将申请的空间释放掉,将_str设置为空指针,你的_size和_capacity要不要置0都无所谓。

讯享网

既然有了析构,那么拷贝构造与也需要我们自己手搓(他们是配套出现的)

1.深浅拷贝问题

我们先看看没有手动实现拷贝构造,使用编译器默认的拷贝构造会发生什么事情。
在这里插入图片描述

在这里插入图片描述
可以看到发生了报错,那这么是为什么呢,我们结合图来一探究竟。
在这里插入图片描述
我们可以看到,这两个对象中_str的地址是一样的。
在这里插入图片描述
这是因为编译器默认生成的拷贝构造是每个字节每个字节的拷贝,这也就导致了s1,s2的_str都指向了同一块空间,当test函数结束,要需要销毁s1和s2,就会先销毁掉s2的_str,当s2的_str被销毁后,这个_str所指向的空间就已经被释放了,这时在销毁s1,就会使一个空间被释放两次,就会发生崩溃。
这种拷贝方式被称为浅拷贝。

浅拷贝就像一个家里有两个孩子,但只有一个玩具,当他们都想玩玩具,且只想自己玩的时候,就会你争我抢,导致玩具损坏。
在这里插入图片描述
可以用深拷贝来解决这个问题,,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了

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

2. 传统写法

 

和构造函数思路一样,都是开辟一块新空间,然后拷贝数据

3. 现代写法

讯享网

这种写法非常巧妙,我们先用 s 的 _str 初始化一个临时变量tmp,然后将 tmp 的 _str 与 this 的 _str 交换,这样就完成了拷贝构造,同时也是深拷贝。
大致过程如下图:
在这里插入图片描述

和拷贝构造类似,也是有传统写法与现代写法。

传统写法

 

现代写法

讯享网

注意:这里函数参数,是传值传参,发生了拷贝构造,这是为了不影响 = 右边的对象。

传统写法中,我们还需要手动的销毁原空间,创建新空间,拷贝数据;但是在现代写法,这些都不需要了,新空间新数据已经被 s 开好了,我们只需要交换就好了。

接下来就开始实现常量成员对象npos与一些常用接口

 

npos的作用就是用来当缺省值,用来表示“到字符串结束”。

讯享网

提供只读字符串,返回 _str 即可, 这个_str 被const修饰,无法改变。

查询容量,实现size()和capacity()即可。

 

我们在使用那部分也讲过,返回对应位置的引用即可。

讯享网

之前已经提到过,现阶段我们可以将迭代器理解成一种指针,通过解引用来访问数据元素。我们定义string类时,使用指针来模拟迭代器。迭代器定义如下:

 

我们只实现普通迭代器与const迭代器,反向迭代器后面再讲解。

讯享网

要想支持范围for,我们的名字必须和库里的名字一样,因为范围for是傻瓜式替换。

我们现在试验下我们自己实现的访问

 

在这里插入图片描述

这个虽然不属于字符串的增加与删除,但是每个增加元素的函数都会用到他,所以我就把它提取到这个模块了

讯享网

设计思路和vs是一样的,比原来的_capacity大我就扩容,比_capacity小都不扩容。

注意:到了C++,我们就要舍弃realloc()这个函数来扩容,全部都自己写,因为realloc无法自动调用构造函数,而new可以。

push_back

 

append

append有很多接口,我们实现一个尾插字符串就可以了

讯享网

operator+=

这个直接复用和就好了。

string += 字符

 

string += 字符串

讯享网

这三个都是在string尾部插入元素,要想直线在任意位置插入元素,那就需要这个函数。

insert

我们同样只实现两个版本,插入字符与插入字符串,这两个版本都是在指定位置之前插入元素

 
  1. insert插入字符

既然要在之前插入字符,那挪动数据的时候,也需要挪动原pos位置的元素。

讯享网

流程如下图
在这里插入图片描述

  1. insert插入字符串
 

注意:我们在挪动数据的时候,我们的结束条件一定不能写 ,因为在头插环境会无线循环。
在这里插入图片描述

在这里插入图片描述
所以就会无限循环

把 end 换成 int 类型行不行呢?
答案是不行的,这是C语言留下来的坑,当级别低的与级别高作为操作符的操作数时,级别低的变量会发生整形提升。
也就是说当end与pos位置比较的时候,end会自动提升为 size_t (unsigned_int) 类型

如果你把 pos 也改成 int 类型就能成功运行,但是我们要结合使用场景,如果我们要插入的字符位置是比正整数最大值大一点的数字,那么我们就会插入到负输上,但字符串是没有负数这个概念的,所以将 end 和 pos 都换成 int 是不行的。

正确方法就是上文代码的方法和,!= 这个操作符不需要比较位置之间的大小,只需要看是否相等,如果相等,就停下来跳出循环。

而 -1 是为了插入字符串,因为我们要在指定位置前插入字符串,如果不 -1,那么就会少挪动一位,导致原本的数据丢失一位。

erase

讯享网
 

当要删除的字符个数大于_size后字符个数,直接在将pos位置置为,然后将_szie更新。

如果小于,那就把后面的值往前覆盖,长度-=要删减的字符个数。

这个就很简单了,直接遍历string就好了,如果是查找字符串,那就直接调用C语言string库的strstr

讯享网

直接在函数里创建一个临时变量,让他来拷贝子串,然后出函数的时候再进行构造函数来进行深拷贝

 

我们直接调用strcmp就可以了,重载加任意一个判断运算符(除),其他就可以直接复用。

讯享网
 
讯享网

是输入流的一个函数,功能是在缓冲区中拿到空格和换行(一般的插入是不会读取的)。

 
讯享网
 

那么这次的分享就到这里结束了
string类还是比较简单的

最后感谢您能阅读完此片文章
如果您认为这篇文章对你有帮助的话,可以用你们的手点一个免费的赞并收藏起来哟

如果有任何建议或纠正欢迎在评论区留言~
也可以前往我的主页看更多好文哦(点击此处跳转到主页)。

小讯
上一篇 2025-05-03 07:33
下一篇 2025-06-10 16:43

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/156564.html