C++ string类


这篇文章主要带大家来使用C++中的string类。以及了解string类在部分编译器中的底层实现。

C++ string类

1. 为什么学习string类?

1.1 C语言中的字符串

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

1.2 面试中

在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

2. 标准库中的string类

2.1 string类

string类文档介绍

在使用string类时,必须包含#include头文件(<string>)以及using namespace std;

2.2 如何创建一个string类的对象

这个时候我们可以查看文档中string类的构造函数

string::string文档介绍

如下就是文档中的string构造函数的申明:

在这里插入图片描述

以及对上面申明的解释:

在这里插入图片描述

但是具体怎么定义还是取决于编译器。

这里对第3种方式做格外补充,当传输实参给len的长度超过字符串的长度,那么将直接截取从pos位置到’\0’,如果不传实参给len的话,编译器采用缺省值npos,npos的值为-1,而-1的补码是七十多亿,而采用size_t 类型存储的-1也就成了最大值,所以结果是直接截取从pos位置到’\0’。

在这里插入图片描述

2.3 如何遍历一个string类的对象

这里提供三种方法

1. for循环遍历
string s1("hello world");
for (int i = 0; i < s1.size(); i++)
{
	cout << s1[i];
}

s1.size()就是返回字符串s1的长度。

s1[i]用到了运算符重载,将[]达到了类似用数组一样的访问效果。

2. 迭代器
  1. 正向遍历

    	string s1("hello world");
    	string::iterator it = s1.begin();
    	while (it != s1.end())
    	{
    		cout << *it;	// 可读也可写
    		it++;
    	}
    	cout << endl;
    
    	const string s2("hello world");
    	string::const_iterator it = s2.begin();
    	while (it != s2.end())
    	{
    		cout << *it;	// 只读不能写
    		it++;
    	}
    
  2. 反向遍历

    	string s1("hello world");
    	string::reverse_iterator rit = s1.rbegin();
    	while (rit != s1.rend())
    	{
    		cout << *rit;	// 可读也可写
    		rit++;
    	}
    	cout << endl;
    
    	string s1("hello world");
    	string::reverse_iterator rit = s1.rbegin();
    	while (rit != s1.rend())
    	{
    		cout << *rit;	// 只读不能写
    		rit++;
    	}
    	cout << endl;
    

这里迭代器的类型很多且很长,我们如果用auto就可以一劳永逸。

3. 范围for
	const string s2("hello world");
	for (auto& ch : s2)
	{
		cout << ch;
	}
	cout << endl;

2.3 string类的常用接口说明

1. string类对象的容器操作

在这里插入图片描述

capacity的扩容法则:

由于标准库并没有规定capacity该如何扩容,所以各大编译器呈现不一样,下面主要介绍两种主流的编译器。(下面的capacity都不包含’\0’)

VS2022编译器的扩容法则:

如下就是一个扩容操作,容量的变化:

在这里插入图片描述

下面来简单的解释一下:

VS会将大小小于16的字符串将放到一个_Buf字符数组里面,如果需要扩容初次扩容将以其_Buf的两倍扩容,之后以1.5倍扩容。

在这里插入图片描述

Linux的g++编译器的扩容法则:

如下就是一个扩容操作,容量的变化:

在这里插入图片描述

我们可以看到,g++编译器是两倍扩容

reverse的使用

reverse的文档

在这里插入图片描述

通过文档,我们可以知道reverse有以下几点法则

  • 当扩容值n > capacity;进行扩容
  • 当扩容值n < capacity
    • 当容量值n > 字符串长度size;具体是否进行缩小看编译器
    • 当扩容值n < 字符串长度size;capacity肯定不会缩小到小于size,具体是否进行缩小看编译器

下面依然介绍两种主流的编译器

VS2022

当容量值n > 字符串长度size;不进行缩小

当扩容值n < 字符串长度size;不进行缩小

总结:VS2022采用空间换时间的方式,都不对capacity进行收缩

Linux的g++

当容量值n > 字符串长度size;进行缩小,缩小的大小满足扩容的整数对齐

当扩容值n < 字符串长度size;进行缩小但不会小于size,依然满足扩容的整数对齐

总结:g++考虑空间上的消耗,都尝试进行收缩

resize的使用

resize的文档

在这里插入图片描述

通过文档,我们可以知道resize有以下几点法则

  • 当 n < size 时;将当前的字符串的值只取前 n 个,但是capacity具体是否变化不确定
  • 当 n > size 时;将超过的部分赋值为字符c (如果没有传参的话,默认为’\0’)
    • n < capacity 时;是否扩容不确定
    • n > capacity 时;进行扩容,按扩容整数对齐扩容
2. string类对象的访问

在这里插入图片描述

operator[] 和 at 的区别

operator[]文档

at文档

这两者使用方式类似

string s("hello world");
s[6];	 // w
s.at(6); // w

两者主要的不同是越界访问的时候:
operator[] 会断言

at 会抛异常

3. string类对象的修改操作

在这里插入图片描述

push_back

push_back文档

push_back就是尾插操作尾插一个字符,不能是一个字符串(用append)

string s("hello world");
s.push_back('a');
append

append文档

append的用法有很多,这里就不依依举例,append的最重要的一个用法是尾插字符串

string s("hello world");
s.append("abc");
operator+=

operator+=文档

push_back与append都可以被operator+=替代,并且operator+=也比较方便好用,所以一般我们都直接采用operator+=

string s("hello world");
s += 's';
s += "abc";

operator+=其实就是一个运算符重载,我们在后面的string模拟实现会有具体体现。

insert

insert文档

在这里插入图片描述

insert就是对字符串插入操作。(尽量不要经常使用)

string s("hello world");
s.insert(0, "abc");
s.insert(0, 1, 'd');	// 在0位置上插入1个'a'	
//s.insert(0, 'a');		// 错误的,没有对应的接口
s.insert(s.begin(), 'e');
s.insert(s.end(), 'x');
erase

erase文档

在这里插入图片描述

insert就是对字符串删除操作。(尽量不要经常使用)

string s("hello world");
s.erase(0, 2);		// 从0位置往后删除2个字符
s.erase(2);			// 删除从2位置往后的所有字符,len默认缺省为最大值
s.erase(s.begin());	// 头删
s.erase(--s.end());	// 尾删
replace

在这里插入图片描述

string s("hello world");
s.replace(5, 1, "%%");	// 将第5个位置其往后的1个字符替换成"%%"
cout << s << endl;		// hello%%world
swap
string s1("hello");
string s2("world");
s2.swap(s1);

在这里插入图片描述

c_str

由于C++兼容C语言,有时候需要调用C语言的接口(比如数据库),但是C语言没有引入string类类型,所以需要用c_str将一个string类型的对象转换成const char*类型

std::string str("hello world");
char * cstr = new char [str.length()+1];
std::strcpy (cstr, str.c_str());	// 将str中的字符拷贝到cstr中,但是这个接口是C语言的
find

find文档

在这里插入图片描述

  • str 、s、c 是需要查找的内容
  • pos 是从对象第pos的位置开始查找,默认为0(即从头找)
  • n 是取内容 s的前n个字符用来查找

在这里插入图片描述

  • 如果查找到内容的话,返回第一个找到的内容在对象的位置
  • 如果没查找到内容的话,返回string::npos(用来遍历每一个查找内容时很有用)
// 将' ' 替换成 "%%"
string s("hello worldhello worldhello worldhello worldhello worldhello world");
int pos = s.find(' ');
while (pos != string::npos)
{
	s.replace(pos, 1, "%%");
	pos = s.find(' ', pos + 2);
}
cout << s << endl;
// 当然直接定义另一个string,然后进行存储变化之后的字符,这种空间换时间的方法更快
rfind

同上,就是反过来查找

find_first_of

find_first_of文档

在这里插入图片描述

和find的功能有点相似,不同的就是它是将 str、s、c中的每个字符作为查找标准来查找。而find是以整个 str、s、c为查找标准来查找。

find_last_of

同上,就是反过来查找

find_first_not_of

查找不属于find_first_of范畴内的

find_last_not_of

查找不属于find_last_of范畴内的

substr

这个其实和python的切片差不多,在str中从pos位置开始,截取n(默认为无穷大)个字符,然后将其返回

string s("hello world");
string subs1 = s.substr(6);		// "world"
string subs2 = s.substr(6, 3);	// "wor"
getline

getline文档

在这里插入图片描述

我们在用cin输入给string的时候,会发现 ’ ’ 就是一个分隔符,如果我们要获取比如"hello world"的时候,实际上确实 “hello”,这个时候就得使用getline。

string s;
cin >> s;			// 输入hello world
cout << s << endl;	// 输出hello

getline用法:

用法一:只传两个参数时,默认 ‘\0’ 为分隔符

string s;
getline(cin, s);	// 输入hello world
cout << s << endl;	// 输出hello world

用法二:传三个参数,最后一个传需要以什么为结束的分隔符

string s;
getline(cin, s, '*');
cout << endl;
cout << s << endl;

在这里插入图片描述

4. 常用的类型转换接口

在这里插入图片描述

记忆技巧:

字符串转换成其他类型就是 string to (需要的类型)

比如转整型就是 string to int 简写就是 stoi,其他同理

其他类型转字符串只需要 to_string就行了

	string s1("123");
	int n1 = stoi(s1);
	long n2 = stol(s1);
	unsigned int n3 = stoul(s1);
	long long n4 = stoll(s1);
	unsigned long long n5 = stoull(s1);
	string s2("123.123");
	float n6 = stof(s2);
	double n7 = stod(s2);
	long double n8 = stold(s2);

	int i = 123;
	string s(to_string(i));

3. string类的模拟实现

C++ string类的模拟实现-CSDN博客

4. 扩展阅读

下面有两篇陈皓大佬的关于string的文章

C++面试中string类的一种正确写法 | 酷 壳 - CoolShell

STL 的string类怎么啦?_stl中的string类程序-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值