C++中string类的基本用法


一、string的默认成员函数

在这里插入图片描述

1. 构造函数

在这里插入图片描述
我们可以看到在C++98中光构造函数就有7个重载,其中框住的三个是需要重点掌握的,下面分别演示一遍。

string();// 构造一个空字符串
string (const char* s);// 用C-string来构造string类对象
string (const char* s, size_t n);// 用C-string的前n个字符来构造string类对象
string (size_t n, char c);// 生成n个c字符的字符串
string (const string& str);// 利用原先的字符串做拷贝构造
// 拷贝str字符串中从pos位置开始的len个字符
string (const string& str, size_t pos, size_t len = npos);

在这里插入图片描述


2.赋值重载

在这里插入图片描述

string& operator= (const string& str);// 将一个string对象赋值给到另一个
string& operator= (const char* s);// 将一个字符串赋值给到string对象
string& operator= (char c);// 将一个字符赋值给到string对象

在这里插入图片描述


二、string的常见容量操作

函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充

1.size、length、capacity

  • 【size】和【length】代表的含义一样都是表示字符串中有几个字符,而【capacity】则表示容量为多少。比如一个教室做多容纳50个学生,现在教室里有20个学生。这里的50就代表是容量【capacity】这里的20就代表【size】和【length】。
  • 可以看到s一共有13个字符(其中包含一个空格),所以【size】和【length】的值为13,而capacity的值为15。这里capacity的值是会随之你插入字符的增多自动扩容的。
    在这里插入图片描述
  • 有人会问那【size】和【length】表示的含义是一样的,那有一个不就行了,为什么还要设计出两个来呢?那是因为【size】是STL中的接口,string出现的比STL要早,也就是说刚开始string只有【length】这个接口,但在STL出来之后为了接口的统一,string就又加上了【size】。

下面我们来探究一下【capacity】

void TestPushBack()
{
	string s;
	size_t sz = s.capacity();
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

在这里插入图片描述

  • 我们可以看到在vs中是1.5倍的扩容逻辑
    在这里插入图片描述
  • 在Linux中是两倍的扩容逻辑
  • 这是因为不同平台下这个STL库的内部实现是不一样的,甚至都是在VS中,使用不同版本的VS也会导致扩容大小不同。

2.clear

【clear】的作用就是将字符串清空将【size】改为0,那它还有什么细节呢?我们用下面的函数进行测试。

void Teststring1()
{
	// 注意:string类对象支持直接用cin和cout进行输入和输出
	string s("hello, bit!!!");
	cout <<"clear前size:" << s.size() << endl;
	//cout << s.length() << endl;
	cout << "clear前capacity:" << s.capacity() << endl;
	cout << s << endl;

	// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s.clear();
	cout << "clear后size:" << s.size() << endl;
	cout << "clear后capacity:" << s.capacity() << endl;

}

在这里插入图片描述

  • 可见对于clear来说,它只会清空字符串中的【size】原来是13,现在清空了变成了0,但对于【capacity】来说是不会有什么变化的,原来是15现在还是15。如果你是这个接口的实现者一定不会去清空这个【capacity】容量的大小,万一后面又需要插入字符呢?此时又需要去进行一个扩容,是非常麻烦的。

3.empty

【empty】就是判断字符串是否为空,如果为空返回真(1),否则为假(0)。
在这里插入图片描述

  • 可见一开始不为空所以为假输出0,clear之后为空是真输出1。

4. reserve

【reserve】的功能是提前为一个字符串开好空间。
在这里插入图片描述

  • 可以看到在vs中我们提前用reserve开了空间之后就不会在进行扩容操作了。但是这里我们发现用的是reserve(100),开了一百个空间,但实际上vs却开了111个空间,而Linux中确实说开100就开100。究其原因还是平台不同底层实现不同。
    在这里插入图片描述

5. resize

void resize (size_t n);
void resize (size_t n, char c);		// 初始化数据为n个c字符

在这里插入图片描述
对于reserve来说只会改变capacity,size原来是多少就是多少不改变,但resize是二者都改变,size变成’n’,capacity遵循vs的扩容规则比n多一些。

  1. 如果 n > 当前的字符串长度,则在末尾插入所需数量的字符以达到n的大小来扩展当前内容,没有指定则填充’\0’。
  2. 如果n < 当前的字符串长度,则会去进行一个删除的操作,删除第n个字符之后的字符。

第一点上面已经验证,第二点如图,我们发现会出现截断字符的现象。
在这里插入图片描述

s.resize(100, 'a');//指定填充的字符为a

三、string的访问及遍历操作

函数名称功能说明
operator[] (重点)返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendrbegin获取最后一个字符的迭代器 + rend获取第一个字符前一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

1. operator[]

char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;

这个接口学过运算符重载的肯定不陌生,本质上就是对[]进行了重载,因此我们可以使用**下标加[]**的方式进行访问string中的数据。并且由于返回的是char类型的引用,因此我们可以直接通过下标加[]的方式进行修改数据。

在这里普通类型的会调用char& operator[] (size_t pos);,而const string s2会调用const类型的[]重载const char& operator[] (size_t pos) const;

void Teststring3()
{
	string s1("hello Bit");
	const string s2("Hello Bit");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;

	// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改
}

在这里插入图片描述


2. 迭代器

下面我们要说以下迭代器,迭代器呢可以说是STL中很重要的一部分。简单来说迭代器就是用来遍历或访问容器中的数据的,我们暂时可以把迭代器想象成指针,通过指针的++或者–加解引用的方式,我们就可以遍历一个数组,或者访问数组中的元素。当然指针只是迭代器中的一种,迭代器要实现的目的就是通过++或者–能够遍历容器中所有的元素,我们数组是一段连续的空间,可以通过指针加一的方式遍历整个数组,但是如果是链表呢?这种情况下通过对每个指针++的操作就无法实现目的了,因此指针就不适合当迭代器了,我们就得封装新的迭代器。
在这里插入图片描述
在这之前呢,我们认识两个最常见的接口函数,即为begin和end

  • begin获取一个字符的迭代器
  • end获取最后一个字符下一个位置的迭代器


在这里插入图片描述
迭代器是是另一种访问string中内容的方式,图示如下,我们使用一个it去保存这个字符串【begin】处的位置,那么在其不断进行后移的过程中,就是在遍历这个字符串,当其到达最后的【end】处时,也就遍历完了,此刻便会停了下来。

void TestIterator()
{
	string s("abcdef");

	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

在这里插入图片描述

  • 迭代器也可以像数组那样在遍历的时候修改内部的元素,it取到的是每个元素的位置,那么对于*it来说即为每个元素
void TestIterator()
{
	string s("abcdef");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		(*it)++;
		it++;
	}
	it = s.begin();
	while (it != s.end())
	{
		cout << *it << endl;
		++it;
	}
}

在这里插入图片描述

begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器,而rbegin和rend恰恰相反,rbegin获取最后一个字符的迭代器 + rend获取第一个字符前一个位置的迭代器
也就是说用begin和end遍历是从前往后遍历,用rbegin和rend就是从后往前遍历。
要注意的地方就是定义要这样string::reverse_iterator rit = s.rbegin();,当然如果记不住这么一长串我们可以直接使用auto来进行自动推导。

void Teststring4()
{
	string s("abcdef");
	string::reverse_iterator rit = s.rbegin();
	// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
	//auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << endl;
		rit++;
	}
}

在这里插入图片描述


3. 范围for

这里的auto我们以前介绍过,它可以自动推导类型,这里如果我们不用auto的话直接使用char也是可以的。

void TestRangeFor()
{
	string s("abcdef");

	for (auto ch : s)
	{
		cout << ch << " ";
	}
	cout << endl;
}

范围for的底层其实是迭代器,也就是在底层使用了迭代器进行遍历。如果没有迭代器,那就不能使用范围for。
也可以像迭代器那样在遍历的时候去做一个修改,值得注意的是要在ch后面加个引用,否则是达不到修改的目的的。
在这里插入图片描述


四、string的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

1. push_back

在这里插入图片描述
就是在当前的字符串后面追加一个字符,值得注意的是只能追加一个字符,不能追加多个字符否则会报错。
在这里插入图片描述


2. append

在这里插入图片描述
这个函数重载了很多,大家可以根据下面的介绍自己操作一下。

string& append (const string& str);	// 追加一个string对象
// 追加一个string对象中的指定字符串长度
string& append (const string& str, size_t subpos, size_t sublen);	
string& append (const char* s);	// 追加一个字符串
string& append (const char* s, size_t n);// 追加字符串中的前n个字符串
string& append (size_t n, char c);// 追加n个字符

	string s1("aaaaa");
	string s2("bbbbb");
	s1.append(s2);
	cout << s1 << endl;
	s1.append(" ");

	s1.append("ccccc");
	cout << s1 << endl;
	s1.append(" ");

	s1.append("hello", 3);
	cout << s1 << endl;
	s1.append(" ");

	s1.append(10, 'e');
	cout << s1 << endl;

自已对照一下。
在这里插入图片描述


3. operator+=

在这里插入图片描述

  1. 拼接两个string类型的字符串
	s1 += s2;

在这里插入图片描述

  1. 拼接一个stirng类型加一个字符串
	s1+="world";

在这里插入图片描述
3. 拼接一个string类型加一个字符

s1+='w'

在这里插入图片描述


4. c_str

在这里插入图片描述
它的功能是返回一个c格式的字符串。有的人可能会说,为什么还要转换成C格式的字符串呢?那是因为我们调用某些函数,在传参的时候必须传的是c格式的字符串,不能传string,比如打开文件的fopen函数。

FILE * fopen ( const char * filename, const char * mode );

因此如果我们这样打开这个文件的话就会报错

void TestCStr()
{
	string str = "test.cpp";
	fopen(str, "r");
}

在这里插入图片描述
因此我们应该使用c_str函数进行转换,这个接口存在的意义就是一座桥,将C++和c关联起来。

FILE* fout =  fopen(str.c_str(), "r");

五、string的非成员函数重载

operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>> (重点)输入运算符重载
operator<< (重点)输出运算符重载
getline (重点)获取一行字符串
relational operators (重点)大小比较

1. operator+

谈起operator+,不知道各位有没有想起operator+=呢?我们可以将二者对比一下。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

二者的最本质区别还是在于这个效率问题,对于==operator+==而言,其底层在实现的时候因为无法对this指针本身造成修改,所以我们会通过拷贝构造出一个临时对象,对这个临时对象去做修改后返回,那我们知道返回一个出了作用域就销毁的对象,只能使用传值返回,此时又要发生一个拷贝因此本接口其实不太推荐读者使用,了解一下即可,尽量还是使用operator+= 来得好。


2. operator>>和operator<<

在这里插入图片描述
流提取,就是我们使用cin >>输入操作的时候一样,控制台要等待我们输入一个值
在这里插入图片描述
流插入,通过去缓冲区中拿取数据,然后将其显示在控制台上。
在这里插入图片描述


3. getline

使用getline我们可以从缓冲区获取一整行的数据
在这里插入图片描述
无论是C语言中的scanf还是C++中的cin每次遇到空格就会直接结束输入,那有没有一种方法在输入的时候遇到空格不结束遇到换行才结束呢?那就是可以使用getline函数。
在这里插入图片描述
第一个参数填cin,第二个填要输入的字符串就行。


4. relational operators

大小比较,这其实就是在底层将==、<、>等等这些运算符进行了重载。

==是将string中的每个字符进行对比,如果所有字符全都一样,那就返回true
<就是将两个字符串中的字符从第一个开始比大小,这里的比大小是按ASCII码进行比较,如果两个串s1和s2,其中s1第一个元素的ASCII码比s2第一个元素的ASCII码小那就返回true否则返回false并停止比较,如果s1和s2第一个字符相同,那就比较第二个字符,以此类推。

简单演示几个
在这里插入图片描述
由于ASCII码中b的比a的大,所以输出的是s1<s2。
在这里插入图片描述


  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

争不过朝夕,又念着往昔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值