一、string的基本概念

1.1string是管理字符数组的类

常见的初始化使用场景:无参构造和拷贝构造

string s1; //无参构造
string s2("hello world");  //有参构造

对string类的总结:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

二、string类模板

2.1重要的运算符重载

2.1.1 string::size

C++ STL string初探:string类剖析_STL

2.1.2 string::operator[]

C++ STL string初探:string类剖析_STL_02

string::operator[]底层原理是在堆上开一个字符数组,在字符数组里面存。

这两个的结合可以解决string类字符串的单个遍历,像数组一样进行遍历。

for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << endl;
	}

我们发现虽然底层在字符数组里面存,但是在打印的时候是\0不会显示打印出来?

for(int i=0;i<s2.size()+1;i++)
{
  cout<<s2[i]<<" ";
}

C++ STL string初探:string类剖析_c++_03

C++ STL string初探:string类剖析_STL_04

通过编写一段上面的代码发现是还是没有显示打印出来,再监视s2发现当i=11时,寄存器临时变量没有数据进行输出打印。

2.1.3 比较思考

那么string这类的这个字符串和char a[]这些字符数组有没有区别?

比如:s1("hello world");     char a[]="hello world"

差别在哪儿:底层差别 s1本质是一个类调用的string::operator[],a[]本质是数组的首地址也就是一个解引用+地址。 



2.2 迭代器

理解成像指针一样的东西。 

如下:

string::iterator it = s1.begin();

iterator是标准库类模板的关键字,需要自己指定限定符,it相当于是s1这个string类字符串的第一个字符。

常见的使用

#include<iostream>
using namespace std;
int main()
{
   string s1("hello world");
   //使用迭代器的对s1的遍历
   string::iterator it=s1.begin();
   while(it!=s1.end())
   {
      cout<<*it<<endl;
      it++;
   }
   
   //另外一个重要的c++11 可以用的遍历方法
   for(auto i:s1)   //这个的auto其实是自动识别的迭代器类型 <=> string:iterator i:s1
   {
      cout<<i<<" ";
   }
  
 return 0; 
}

那么迭代器可以遍历string的字符串,那么能修改嘛?

标准库的字符串容器std::string是可以进行修改的。

for (auto i : s1)
    {
        i = 'x';
        cout << i << " ";
    }

C++ STL string初探:string类剖析_c++_05

三、string类的常用接口

3.1string类对象的常见构造

C++ STL string初探:string类剖析_c++_06

1.string() ----->调用的构造函数初始化为空字符串

2.string(const string& str) ----->调用的拷贝构造函数

3.string(const string& str,size_t pos,size_t len=npos) ---->将从str这个字符串的pos位置后len个长度进行拷贝构造函数

4.string(const char* s)    ----->用一个字符数组(c-string)来构造对象

5.string(const char* s,size_t n)  ---->选取s字符数组前n个进行拷贝构造

6.string(size_t n,char c)   ---->将string对象初始化为n个字符c

三个重点掌握的string类构造运行实例

#include<iostream>
using namespace std;
int main()
{
	string s1;
  cout<<s1<<endl;
  const char a[]="hello world";   //const可加可不加 不加的话就是权限缩小
  string s2(a);
	cout<<s2<<endl;
  
  string s3(s2);
  cout<<s3<<endl;

	return 0;
}

C++ STL string初探:string类剖析_string_07

3.2string类对象的容量操作

3.2.1 size

功能:返回字符串有效字符长度

string s1("hello world");
cout<<s1.size();
//输出结果为10


3.2.2 length

功能:返回字符串有效字符长度

string s1("hello world");
cout<<s1.length();
//输出结果为10

size()和length()的底层实现是一样的,然后实现的功能也一样,觉得有点冗余,但是size()这个设置是为了与其他标准库保持一样的函数接口设置的。


3.2.3 capacity

功能:返回空间总大小(换句话说,就是返回开辟的空间大小)

string s1("hello world");
cout<<s1.capacity();
//输出15

为什么是输出15呢,我们知道string实际也是顺序表,顺序存储结构,那么也就是扩容的路线。vs下和linux下的扩容策略不一样,vs下是1.5倍。


3.2.4 empty

功能:检测字符串释放为空串,是返回true,否则返回false。

int main()
{
	string s1;

	if (s1.empty())
	{
		cout << s1.empty() << endl;
	}
	string s2("hsq");
	if (!s2.empty())
	{
		cout << s2.empty() << endl;
	}
	return 0;
}

C++ STL string初探:string类剖析_c++_08

3.2.5 clear

C++ STL string初探:string类剖析_标准模板库_09

功能:清空有效字符

string s2("hsq");
	s2.clear();
	cout << s2 << endl;
//输出为空

思考:我把有效字符清空了,那么我的空间会有影响?

测试:

string s2("hsq");
	cout << "clear前容量" << s2.capacity() << endl;
	s2.clear();
	cout << s2 << endl;
	cout << "clear后容量" << s2.capacity() << endl;

C++ STL string初探:string类剖析_标准模板库_10

可见,clear只进行有效字符的清理工作,对string对象字符串的开辟的内存空间是不影响的。


3.2.6 reserve 

C++ STL string初探:string类剖析_STL_11

功能:为字符串预留空间(适用于已知需要多少空间的情况,减少扩容操作)

int main()
{
	string s1;
	cout << s1.capacity() << endl;
	s1.reserve(100);
	cout<< s1.capacity() << endl;
  
	return 0;
}

C++ STL string初探:string类剖析_标准模板库_12

可见,reserve函数可以提前预留空间,改变的底层的内存空间。

那么预留空间小于当前string对象的空间会发生什么情况呢?

测试:

int main()
{
	string s1("hello world");
	cout << s1.capacity() << endl;
	s1.reserve(2);
	cout << s1.capacity() << endl;


	return 0;
}

C++ STL string初探:string类剖析_STL_13

当预留空间小于string对象的内存空间,是不会进行预留空间的操作,也不会缩减空间,但是在其他环境下可能会进行缩容。

3.2.7 resize

C++ STL string初探:string类剖析_STL_14

功能:将有效字符的个数改成n个,多出的空间用字符c填充

int main()
{
	string s1("hello world");
	cout << "原始的有效字符个数" << s1.size() << endl;
	cout <<"原始的内存空间容量" << s1.capacity() << endl;
	//把有效字符个数改成100个 
	s1.resize(100);
	cout <<"字符串" << s1 << endl;
	cout << "有效字符个数" << s1.size() << endl;
	cout << "内存空间容量" << s1.capacity() << endl;
	cout << "-------------------------------" << endl;
	//把有效字符个数改成100个 未填充的填充为'a'
	string s2("hello world");
	cout << "原始的有效字符个数" << s2.size() << endl;
	cout << "原始的内存空间容量" << s2.capacity() << endl;
	s2.resize(100,'a');
	cout << "字符串" << s2 << endl;
	cout << "有效字符个数" << s2.size() << endl;
	cout << "内存空间容量" << s2.capacity() << endl;
	cout << "-------------------------------" << endl;

	return 0;
}

C++ STL string初探:string类剖析_c++_15

C++ STL string初探:string类剖析_STL_16

通过两次相同函数的重载调用发现,当进行了resize那么会改变size大小是进行增大size大小的话,capacity相应的按照自己扩容的策略进行扩容;当是进行size减少(有效字符数据可能丢失),那么底层的capacity容量不变。当传入第二个字符参数‘a’,那么字符串中未被填充的就会被填充为a;如果不填,那么就是用0来天聪多出的元素空间。

3.2.8 resize和reserve的对比区别:

#include<iostream>
using namespace std;
int main()
{

	string s1("hello world");

	s1.reserve(100);  //单纯的开空间 改变的capacity
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;


	s1.resize(100);   //开空间+填值
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;



	return 0;
}

C++ STL string初探:string类剖析_c++_17

reserve:单纯的开空间 改变的capacity,当是要缩小的时候是不改变capacity和size的。

resize:开空间+填值(默认为\0) 或者 删除数据 不缩容(空间大小还在)

3.3string类对象的访问及遍历操作

3.3.1operator[] 

C++ STL string初探:string类剖析_STL_18

功能:返回pos位置的字符,const string类对象调用

这里在上面进行了分析,是string重要的运算符重载,使得string对象的字符串,能像数组一样进行直接访问和遍历,极大的解决很多操作的难点

int main()
{
	string s1("hello world");

	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}

	return 0;
}

C++ STL string初探:string类剖析_STL_19

 3.3.2 begin+end /rbegin+rend

begin+end :

功能:begin获取一个字符的迭代器,+end获取最后一个字符下一个位置的迭代器。

rbegin+rend:

功能:begin获取一个字符的迭代器,+end获取最后一个字符下一个位置的迭代器。

int main()
{
	string s1("hello world");
	
	auto it = s1.begin();
	//访问
	cout << *it << endl;
	//遍历+访问
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}

	return 0;
}


C++ STL string初探:string类剖析_string_20


s1.begin() 是指向s1这个string对象的第一个字符。可以差不多理解为c语言的指针,但是从根本上不是指针。


3.3.3 范围for

c++11支持的新的遍历方式

实现的底层其实还是用的迭代器。

for(auto i:x)
{
  cout<<i<<" ";
  
}

3.4 string类对象的修改操作

3.4.1 push_back(尾插)

push_back这个在标准模板库里面是不陌生的,很多都有这个接口函数。

功能:在字符串后尾插字符c

int main()
{
	string s1("hello world");
	s1.push_back('a');
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_标准模板库_21

3.4.2 append

相比push_back这个是在字符串后面追加一个字符串

int main()
{
	string s1("hello world");
	s1.append("hsq");
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_STL_22

3.4.3 operator+=

功能:在字符串后面追加字符串strC++ STL string初探:string类剖析_STL_23

这里的运算符重载,简直就是完美的设计,用一个运算符重载+函数重载解决了前面push_back和append所做的事情。

功能:在字符串后面追加字符串str

int main()
{
	string s1("hello world");

	s1 += 'a';  //等价于 s1.push_back('a');
	cout << s1 << endl;
	s1 += "hsq";  //等价于 s1.append("hsq");
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_c++_24

3.4.4 c_str

功能:返回C格式字符串

在我看来string的内置成员就是用c_str来存储字符数组。

int main()
{
	string s1("hello world");
	const char* arr = s1.c_str();  
	for (int i = 0; i < s1.size(); i++)
	{
		cout << arr[i] << " ";
	}

	return 0;
}

注意:用一个字符数组接收的时候要加const,不加就是权限扩大,那就会报错。

C++ STL string初探:string类剖析_STL_25

3.4.5find/rfind

C++ STL string初探:string类剖析_STL_26

C++ STL string初探:string类剖析_标准模板库_27

find功能:从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置

rfind功能:从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置

有时候,在做oj题目的时候,find可以解决很多棘手的问题,哈哈哈我只能说find大法好。find可以解决需要通过遍历来找到对应的字符。

 用find的简单查找方法:

int main()
{
	string s1("hsq");
	cout << s1.find('q') << endl;
	cout << s1.rfind('q') << endl;

	return 0;
}

复杂的查找方法:

for (int i=0;i<s1.size();i++)
	{
		if (s1[i] == 'q')
		{
			cout << i << endl;
		}
	}

3.4.6 substr

功能:在str中从pos位置开始,截取n个字符,然后将其返回。

如果没有第二个位置的参数,那么默认截取是从pos位置开始到结束

int main()
{
	string s1("hello world");
	cout<<s1.substr(2)<<endl;  //从2这个位置开始往后到结束全部截取并返回

	cout<<s1.substr(1, 2)<<endl;  //从1这个位置后两位进行截取并返回

	return 0;
}

C++ STL string初探:string类剖析_c++_28



3.5string非成员函数

3.5.1 operator+ 

C++ STL string初探:string类剖析_string_29

功能:运算符重载,加法运算,尽量少用,传值返回,导致深拷贝效率低。

#include<iostream>
using namespace std;
int main()
{
	string a("hsq");
	cout << a + "hsq" << endl;    // +字符串
	cout << a + 'a' << endl;      // +字符
	string b("hello world");
	cout << (a + b) << endl;      //+string对象


	return 0;
}

C++ STL string初探:string类剖析_string_30

3.5.2 operator>>

C++ STL string初探:string类剖析_标准模板库_31

功能:输入运算符重载

int  main()
{
	string a;
	cin >> a;  //读到空格停止

	cout << a << endl;

	return 0;
}

3.5.3 operator<<

功能:输出运算符重载

int  main()
{
	string a;
	cin >> a;  //读到空格停止

	cout << a << endl;


	return 0;
}


3.5.4 getline

C++ STL string初探:string类剖析_标准模板库_32

功能:获取一行字符串

为什么有getline?用cin不就好了?

cin特性是当读到空格就会停止,那么当需要包括空格之外的一行都要读,那么就得用getline了。

#include<iostream>
#include<string>  //注意这个要加string这个头文件
using namespace std;

int main()
{
	string s1;
	getline(cin, s1);

	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_string_33


3.5.5 relational operators

功能:比较大小

实际运用的是>、>=、<、<=这些符号,比较正确就是返回1,不正确就是返回0

int main()
{
	string a("123");
	string b("124");
	cout <<( a > b) << endl;

	return 0;
}


C++ STL string初探:string类剖析_c++_34