您的位置:首页 > 娱乐 > 明星 > c++ string解析及其实现

c++ string解析及其实现

2024/12/23 1:25:39 来源:https://blog.csdn.net/2301_80772499/article/details/140799159  浏览:    关键词:c++ string解析及其实现

因为字符串是固定长度,不好进行操作,因此c++就用类将字符串进行了封装让其变得方便实用。

要深刻了解string,我们必须要熟练掌握类的使用,如果还有疑问可以看这一篇博客:c++ 类 (要学习类这一篇就够了 )

string

#include<string>

string是需要引用头文件<string>的,同时使用官方命名空间std。

string的函数

首先就要了解string构造相关的函数:

默认,传参,拷贝,赋值,析构

对于迭代器版本我在下面再统一介绍

默认构造

默认构造就是构造一个"",里面只包含了'\0',但是它的大小是0,说明'\0'不作为它的长度。

拷贝构造

第一个拷贝构造就是传统的(里面有隐式类型转换,创造一个string的临时对象来进行拷贝,但是编译器把它进行优化了,相当于string a("123456")),第二个就是加了一个限定范围(从它的哪个地方开始拷贝,拷贝多长)。其实两个拷贝构造能合成一个缺省参数的。string(const string&str,size_t pos=0,size_t len=npos)


传参构造

这三个构造函数都可以归类于传参构造,接下来我们来举例一下:

第一个就是用一个字符串来进行构造,第二个是用字符串的前n个字符来进行构造,第三个是用n个c字符进行构造

其他构造方法
迭代器构造

具体在下面迭代器块详细介绍:

初始值设定项列表

这种是c++11出来的东西,主要是这样用:

赋值

赋值也很简单:

值得注意的就是赋值和拷贝都是深拷贝,需要重新申请堆区的内存。

析构

析构需要释放堆区的内存,这是重点。

迭代器

看这里需要知道迭代器是什么,可以看看这一篇:迭代器的介绍

里面详细介绍了string的迭代器,这里大致再复习一下。

迭代器有iterator(正向迭代器)、const_iterator(不可修改值的正向迭代器)reverse_iterator (反向迭代器) const_reverse_iterator(不可修改值的反向迭代器)

这里可以就看成指向数组内容的指针,只是typedef了一个别名而已。

我们可以通过解引用来访问值,通过调用begin(),end()来调用正向迭代器的开头和结尾,同时我们可以用rbegin(),rend()来调用反向迭代器的开头和结尾。

其实就把它成连续内存的特殊指针就行了,如果是反向迭代器就是反着走是正方向。

string的其他成员函数

我们分块来讲:

首先开头的迭代器成员函数我们已经了解到了

Capacity

了解函数前我们要大概了解string的成员变量是什么:

string的结构其实和顺序表是差不多的,用一个指针只想堆区内存的成员,然后是一个size记录当前字符串的长度,然后是用capacity记录申请内存的长度,即当前最大能存储字符的长度,超过这个长度就要扩容。

然后是;

size/length

这些是关于空间的函数,第一个就是获取当前的字符串实际长度。第二个和第一个是同义,为什么会有两个相同功能的函数是因为原先只有length的函数,但是并不适用于其他容器,其他容器空间不是线性的,所以用的size更加的宽泛,然后string为了和后面的统一就又加了size函数。

max_size

这里的max_size就是能够装多大的长度,一般是长度为len,size_t len=-1。因为类型转换会变成1111111111,就是size_t的最大储存长度。

resize

re一般在英语里面表示"再"的意思,这里就是再次定义空间意思,这里的空间不是capacity的空间重新申请,而是增加或减少size的长度。

首先是缩短:

void resize(size_t n)一般这个函数是用来缩减长度的。

缩短就是直接减少长度。

增长:

resize(size_t n,char c=0)

这里的n不是要增加或者减少多少长度,而是改变后的长度。第二个参量是增长要的字符,一般是缺省值'\0'

增长不会改变原有的内容,而是从末尾处用字符c开始增长。

这里我们发现它和字符串的打印还略有不同,不是到'\0'终止,而是继续打印('\0'是打印不出来东西的),这里是根据size来打印。

capacity

这个就是返回最大储存长度的值

如果size和capacity相等了就会扩容,那么扩容会有不同扩的方法,在vs上面是1.5倍扩:

这里看上去不是标准的二倍是没有吧'\0'算进去。

这里我们还发现在开始第一次是二倍扩的,这是因为我们开始的16长度不是在堆区申请的内存,而是在栈区的内存,如果满了就再申请堆区的。

如果在Linux下,就是2倍扩容。而且初始不会给内存。

reserve

这个就是重新定义capacity的长度,那么也有缩短和增长的问题,在VS和linux下面是略有不同的:

在vs上:

首先是增长,增长会根据1.5倍扩容来reserve ,假如你现在是64长度的capacity,那么你要扩到80,他会给你扩到96

然后是缩短:vs是不会进行缩短的,因为缩短也会进行重新内存申请,然后把原来的数据拷贝过去。可能考虑到这一点所以不缩。

在Linux上:

Linux上面就比较自由,你要它扩多少它就扩到多少,如果是缩短它也会缩,如果你让它缩的长度小于size会所到等于size为止。

clear

clear就是清除数据,只留一个'\0'

empty

这个函数是判断string是否为空

shrink_to_fit

这个函数就是让capacity缩到和size一样的长度。和linux的reserve的缩短一样的。

Element access

operator[]

重载[]方便像数组一样进行下标访问。

如果超出范围会assertv

at

这个和opertator[]的作用是一样的,唯一不同就是超出范围会抛异常不是assert

back

根据类是否const来返回最后一个数据的引用

front

同上面一样,除了这里返回第一个

Modifiers:

operator+=

这个就是在原串后面增加一个串的操作服重载:

前面三个好说,就是加string、数组、字符。

第四个是c++11的:

用一个括号括起来里面放数据的做法就是initializer_list类型

append

和+=重载是一样的,往后追加内容,但是有更多的操作:

使用是和string的构造函数一样的,只是一个是用指定内容构造一个是用指定内容追加在末尾。

push_back

这个就是往后面压入一个字符

assgin

assgin就是重新给string内容的意思,和resize不同的是,它是会将之前的值全面覆盖。

这里的8个重载也和构造函数的几个重载和append的7个重载一样的。(里面的右值引用我不讲,因为我还没学到,后面学到了再来补充)

insert

插入内容的意思,涉及的重载和上面的assgin、append、构造函数一样的。

支持只插入一个字符或者字符串

下面是举例:

erase

和insert函数是对应的

第一个参数就是传开始删除的位置,可以传迭代器,也可以传下标。第二个是终止的位置不传一般是删到末尾。

replace

顾名思义,是替换内容的意思,这里前两个参数

这些其实都是一个逻辑用法,前两个参数传要替换的区间,可以传下标区间,可以传迭代器区间;

后面参数其实和构造函数、append、insert传参是一个模式。

swap

swap函数是交换两个string的内容,但是它不是用的官方的普通swap。

我们用下面的代码模拟一下

红框的就是官方的常用swap,构造一个临时变量,然后再赋值,这样的操作给string将会有一次拷贝构造两次赋值重载,会有很大的时间损耗,每次都要开新空间。所以类里面实现是交换每个参量的值。为了防止有人用默认的swap,所以又写了一个全局的string专用swap函数(如果有现成的函数,和它同类型的模板是不会去实例化的,会直接用现成的)。

pop_back

就是删除最后一个元素。

String operations:

c_str

返回c类型的字符串。

data

跟c_str是差不多的,返回字符串

get_allocator

获取内存池,这里内存池是指先从堆区申请一大块内存,然后要内存申请就在内存池里面申请,增加申请的速度。

copy

将从pos下标开始的,长度为len的内容拷贝到s字符串上面去。

find

寻找对应的字符或者字符串,返回第一个字符的下标

rfind

rfind就是反向查找

find_first_of

这个函数就是find的加强版,输入一个字符串或字符,只要是里面其中之一的字符,就会返回。

举例:

要把所有aeiou的字符屏蔽掉。

find_last_of

这个就是反着开始进行find_first_of

find_first_not_of

这个就是找出了那几个字符其他的字符。

find_last_not_of 

这个就是反着进行find_first_not_of

substr

取出string里面从pos下标点,长度为n的数据并返回字符串

举例:

compare

进行比较,和strcmp的返回值一样的。

用的不是很多,因为可以用运算符重载

Member constants

这个和max_size是一个东西,返回可以创建string的最大长度

它其实是一个成员变量 是  const static size_t npos=-1。

Non-member function overloads(全局函数)

operator+

实现两个串的相加

逻辑运算符

这个很简单就不说了,这个比compare好用的多。但是这个没有compare的局部比较

operator<<和operator>>

这个就不用多说了,流插入和流提取。

getline

operator>>的流插入是默认空格和换行停止读取的,所以针对于"I love you"这种有空格的字符串就不能很好的运行。所以就有了getline

这里的delim就是遇到什么停止读取,默认是'\n'停止读取。

到这里所有和string相关的函数就说完了,大家可以理解性记忆。

string的实现

较为完善的string实现

首先我们写的string类是写在.h文件里的,因为在类里面的函数是内联函数,所以就直接在类里面写,如果遇到函数行数比较多的,就可以在.cpp里面写

我们可以自己创建一个命名空间来写我们的string类,与标准库里面的string类进行区分。

string我只实现一个简单版本的,没有内存池,没有vs里面的初始16长度的栈区内存,那么成员变量就有:char*_str size_t _size  size_t _capacity const static size_t npos=-1;

先是构造函数:

这里我们不将'\0'计入capacity中,所以我们申请内存要额外再给‘\0’申请一字节的空间。即new char[_capacity+1]

然后是一些其他的成员函数:

类外函数我们就只实现这三个,然后是迭代器的typedef。

一些比较长的函数就在另一个文件里面:

那么一个相对完整的string就模拟出来了。

面试里面简单string的实现

面试肯定不会让你实现一个这么长的string,只会让你将一些基础的功能实现出来。

那么我参考了陈皓的文章C++面试中string类的一种正确写法 | 酷 壳 - CoolShell来进行如下讲解:

首先我们可以不需要capacity和size这两个成员变量,只要进行简单的构造,赋值,交换,返回大小这些操作即可:

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com