STL之string

1. 为什么学习string类?

1.1 C语言中的字符串

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
自己管理,稍不留神可能还会越界访问。

1.2 两个面试题(暂不做讲解)

https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e?tpId=13&&tqId=11202&rp=6&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

https://leetcode-cn.com/problems/add-strings/

2. 标准库中的string类

2.1 string类(了解)

前置:
1、在使用string类时,必须包含#includ,这里与C语言区别没有.h
2、string里的字符串是以类似于字符顺序表的形式存储的。

首先在学习STL这一节查文档必不可少,有两个文档供我们查找,一个是cplusplus,还有一个是官方文档,但是官方文档对初学者会特别的不友好,所以我们选择cplusplus。我们怎么查呢?在这里插入图片描述
大家按照图中的方式,先打开reference,然后打开other中的string。这里面就是string的所有内容,里面我们只会选一些重要的来学习,其他的大家可以自己看看。

我们首先学一些基础操作:
在这里插入图片描述

我们需要点进string。
在这里插入图片描述
然后我们再点进下面的constructor
在这里插入图片描述
c++98里的函数就是我们目前需要学的

那我们就先来简单的使用一下string,再来学上面的函数

#include<iostream>
using namespace std;

int main()
{
	string s1;//默认构造
	string s2("1111111");//传参构造
	string s3(s2);//拷贝构造

	cin >> s1;
	cout << s1 << endl;

	return 0;
}

在这里插入图片描述
大家可以看到string是重载了流插入与流提取的,所以我们是可以直接使用cin>>与cout<<的。

现在我们开始看文档,在这个文档里下面的英文部分是对上面函数的使用方式说明
在这里插入图片描述
在读文档时,这里的技巧是“蒙”,当然不是毫无根据的蒙,我们是有根据的蒙,例如 string (const string& str, size_t pos, size_t len = npos):
pos在英文中是position的前面的部分,那我们就猜测它是定位的意思,len是lenth的缩写,那我们就猜测是长度的意思,那连起来我们就猜测这个函数是拷贝指定位置后的len个长度到目标string里,那我们就来试一下?

在这里插入图片描述
这里我们有个问题,上面我们只拷贝了5个字符,那要是我们拷贝6个或者更多(字符长度不够)会发生什么呢?这个时候我们需要看文档,看文档需要我们下来自己完成,这里我们直接给结论,只会将该字符串读完,并且如果我们我不写len的长度,该函数也会有一个npos,它是直接从pos位置直接将字符串读完。
在这里插入图片描述
大家可能会好奇npos是什么呢?
在文档中可以看到,npos=-1,但这里是补码,它转换为无符号会变为整形最大值。
在这里插入图片描述

前四个我们其实已经完成了,我们来看第5个函数(string (const char* s, size_t n);)(拿前n个字符初始化),这里就不带大家一步一步来看了,大家自己读一下文档。
在这里插入图片描述

string (size_t n, char c),这个函数我们通过读文档大概可以知道,它的意思是n个字符c来初始化。
在这里插入图片描述

然后我们来看string的析构,就是进入下面这个。

在这里插入图片描述
上面我们说了,string底层是动态开辟的数组,那我们需要析构吗?析构函数是自动调用的,这一块我们可以不用管,当然有兴趣可以下来自己了解。

下面我们来看一个string特别牛的东西:
在这里插入图片描述
它的底层大概是这样的:

class string
{
private:
	char* _str;
	size_t _size;
	size_t _capacity;
public:
	char& operator[](size_t i)
	{
		return _str[i];
	}
};

也就是我们可以直接通过[]来访问任意位置的字符,并且我们返回的时引用,所以我们是可以修改的。
示例:
在这里插入图片描述
这一块在后续的学习中会非常的方便。

下面我们来学习遍历输出字符串:
(1)下标加[]
首先string是有很多函数的,其中有个叫做size的可以返回字符串中字符个数
在这里插入图片描述
其他的函数大家下来自己去看文档来使用,那我们就可以通过上面的方式来遍历输出字符。

(2)迭代器(***重点)

string::iterator it = s1.begin();
while (it != s1.end())
{
	cout << *it << ' ';
	it++;
}

我们可以将it理解为指针,但它并不一定是指针,begin是首字符的地址,end是尾字符下一个字符的地址(也就是’\0’),当it不为指针时,* 可能是运算符重载 (在list里我会介绍)

迭代器是一个很重要的东西,因为所有的容器,迭代器都可以使用,例如链表:

list<int> lt = { 1,2,3,4,5,6,7 };
list<int>::iterator lit = lt.begin();
while (lit != lt.end())
{
	cout << *lit << ' ';
	++lit;
}

上面的iterator我们称之为正向迭代器,此外还有一个东西叫做反向迭代器,reverse_iterator,它的用法如下:

在这里插入图片描述
反向迭代器可以用于链表,图,树等结构。

const迭代器,如果我们的字符串加了const修饰,那么我们的迭代器就不可以使用了,因为在上面的迭代器中我们的串可读可写,这时候就需要const迭代器(只可读)

const string s1("hello world");
string::const_iterator cit = s1.begin();
while (cit != s1.end())
{
	cout << *cit << ' ';
	cit++;
}
cout << endl;

const反向迭代器是一样的,大家自己下来写一下就行。

总结一下:迭代器一般有四种。

(3)范围for

下面讲。

然后我们来看capacity部分
在这里插入图片描述
1、首先lenth与size其实都是一样的(求字符长度),lenth是c++早期为了适应c语言而使用的,但在c++更新了STL后,在图等数据结构中lenth是不太合理的,所以c++就更新了size,总而言之size适用范围更广。

2、max_size是字符能开多少空间,这个用处不大

3、capacity,字符数组当前容量(不包含\0)

在这里插入图片描述
这段代码可以简单的来观察一下容量的变化,大家可以看到从15到31,实质上是16->32,因为数组最后一个空间存“\0”,只有这一部分是成2倍增长的,之后是成1,5倍增长的,这是为什么呢?

class String
{
private:
	char buffer[16];
	char* _str;
	size_t _size;
	size_t _capacity;
public:
};

string在官方库里是有一个buffer的数组的,如果字符串小于16就存于这里,如果大于就会存在下面的动态数组里,buffer就会被销毁。在linix环境下是标准的2倍增,且没有buffer数组。

当然扩容不建议扩的次数太多,因为扩容也会有消耗,所以c++引入了reserve(将空间一次性开好)
在这里插入图片描述
如果我们开100空间,这个100是不包括\0,所以如果不整数对齐的话也会开101。
此外如果我们要申请的空间如果小于当前的capacity,那空间会不会缩水呢?
结论:一般是不会的,但是不能
n<10不会缩
10<n<20不确定
大于20一定会扩
4、clear(清空)
在这里插入图片描述
这里明白clear清空,只清空数据,不清空容量就行了。

5、empty(判空)

这里大家自己简单了解一下就行。

6、shrink_to_fit(缩容),将capacity缩到size

2.2 auto和范围for(重点)

在这里插入图片描述
这里的auto在c++里叫做“自动推导”,它也很好用,它的特性是自动赋值,自动迭代,自动结束。
它虽然看起来很厉害,但它的底层就是迭代器,所以所有的容器也支持范围for。

除此之外迭代器是支持运算的:
在这里插入图片描述
大家可以看这里,迭代器转换后原来的字符串被改变了,但是我们让范围for恢复却并没有成功,这是因为ch是s1的拷贝,如果我们希望其能够改变的话,需要加&引用。
在这里插入图片描述

                                auto关键字
在这里补充2个C++11的小语法,方便我们后面的学习。
1、在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个
不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型
指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期
推导而得。
2、用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际
只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
3、auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
4、auto不能直接用来声明数组

auto最大的作用是简化代码。
此外auto还可以用来简化数组的输出,例如:

int arr[] = { 1,2,3,4,5 };
for (auto& it : arr)
	cout << it << ' ';
cout << endl;

总结一下范围for适用于容器与数组。

我们再介绍一个东西叫做typeid,这个函数可以来查看变量的类型,例如:

在这里插入图片描述

2.3 string类的常用接口说明(注意下面我只讲解最常用的接口)

1、插入型接口。
在这里我只简单的介绍一下字符插入与字符串插入;
(1)push_back(1个字符插入)
(2)append(字符串插入)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
这两个接口实际用的比较多,但是string重载了+=运算符,我们可以通过+=来插入字符或字符串。
在这里插入图片描述

上面的函数都是尾插,我们如果要实现任意位置插入怎么办呢?
(3)insert(给定位置插入)
在这里插入图片描述
这里比较冗余,我们只需要记住3、4这两个方式即可,一个是在pos前插入一个字符,一个是插入n个字符,如果pos为字符串首个位置,这就是头插了。

string str1;
str1.insert(0, "hello school");//头插
cout << str1 << endl;
str1.insert(12, "!");//尾插
cout << str1 << endl;

(4)erase(删除字符或字符串)
在这里插入图片描述

void test4()
{
	string str("hello world");
	str.erase(0, 1);//第0个位置开始,删除一个字符,也就是头删
	str.erase(0, 3);//删除3个字符
	cout << str << endl;

	str.erase(str.begin());//迭代器版本头删
	cout << str << endl;

	str.erase(--str.end());//尾删
	cout << str << endl;
}

(5)replace(替换)
在这里插入图片描述
在这里插入图片描述
(6)find(查找字符)
在这里插入图片描述
这个函数我们是需要关心它的返回值的。
在这里插入图片描述
这里的意思就是如果找到了就返回第一个所找字符的下标,如果没有找到的话就返回npos。
在这里插入图片描述
上面替换这道题当空格特别的多的时候,程序就会特别的低效,我们可以使用另一种思路:

//现在有这样一个题目就是将字符串中空格全部替换为%,这时候就可以使用find+replace。
string str("h l l o !");
size_t z = str.find(" ");
while (z != string::npos)
{
	str.replace(z, 1, "%");
	z = str.find(" ",z+1);//z是替换的位置
}
cout << str << endl;
//第一种思路,如果替换不多可以使用

string tep;
for (auto e : str)
{
	if (e == ' ')
		tep += '%';
	else
		tep += e;
}
cout << tep << endl;
//第二种以空间换时间

(7)rfind从尾查找,substr(字串)

string str("test.cpp");
size_t pos = str.rfind('.');
string sub = str.substr(pos);//从pos开始取子串,如果后面不给参数,默认取完
cout << sub << endl;

(8)
在这里插入图片描述

这四个接口大家下来自己读文档解决。(不太重要)
(9)非成员函数
在这里插入图片描述
第一个重载+,为什么这里将它设置为非成员函数呢?这是为了满足如下操作:
在这里插入图片描述
如果将其设置为成员函数,我们之前讲过成员函数的第一个参数是隐式的this,那样就不能字符串+string了。

swap就是直接交换即可。

最后就是getline这个函数
我们需要在题目中来看:
在这里插入图片描述
这道题我们直接使用rfind找到最后一个空格的位置,然后用size来减即可。
在这里插入图片描述
大家可以看到正确但是应该是1,但是这里是5,这是为什么?
在流输入中编译器会默认使用空格或者\0来分割字符串,空格会存在缓冲区,并且空格不会被使用,所以s1只存了asull这5个字符,所以这里需要我们使用getline这个函数
在这里插入图片描述
它的作用是默认将\0作为字符串结束标志,此外getline是支持其他的字符串结束标志的

在这里插入图片描述
我们只需要在最后补上我们需要作为字符串结束标志的字符即可。

这一部分就是string的使用介绍,下一章我们就来尝试来写string。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值