string类
概念
string类是C++中表示字符串的结构,可以将其认为是C语言的字符串与其相关的函数封装在一起实现的类。避免了用户自己管理底层空间以及越界访问。
string类的常用接口
1、string类对象的常见构造类型
int main()
{
string s1;
string s2("hello world!");
string s3(10, 'x');
string s4(s2);
return 0;
}
2、string类对象的容量操作
(1) size()函数
size()函数是用于返回字符串有效字符长度。
(2) capacity()函数
capacity()函数用于返回对象的空间总大小。
可以看到,s1的字符总数为12个,而容量为15个,说明在开辟空间的时候编译器默认给对象的容量是15个。
(3) empty()函数
empty()函数用于检测字符串是否为空,是返回true,非空返回false。
(4) clear()函数
clear()函数用于清空有效字符。
可以看到,在使用clear()函数,s1变为空字符串。
(5) reserve()函数
reverse()函数用于为字符串预留n个空间,可以看作是在对象中增加n个空间,减少后续的开销。这里分为两种情况:
a. 预留的空间大于可用空间
s1本身的容量为15个,在用reserve函数增加100个之后,变成了111个。这里没有变成115个是因为,s1对象本身只用可11个有效空间,reserve函数将没有用到的空间也当作预留的空间。
b. 预留的空间小于可用空间
这里s1对象本身用了11个空间,此时我们用reserve函数将其改为5个,可以看到这样是不行的,当改变的空间大小小于底层的空间总大小时,reserve函数不会改变容量大小。
(6) resize()函数
resize()函数用于将有效字符的个数改为n个,如果n大于有效字符的个数,没有给定字符默认用’\0’填充,如果给定字符就用指定字符填充。
int main()
{
string s1("hello world!");
s1.resize(20);
string s2("hello world!");
s1.resize(20, 'a');
return 0;
}
s1调用resize()函数没有指定字符,编译器默认用’\0’代替。
s2调用resize()函数时指定字符’a’,编译器将多出的空间用’a’填充。
4、string类对象的访问及遍历操作
(1) operator[]
在string类中,对[]进行了重载,本身string类实例化之后对应的是一个对象,但是将[]重载之后,就可以通过"[]"像数组一样来访问对象的内容以及对内容进行修改,也就是返回pos位置的字符和修改pos位置的字符。
由上图可以看出,通过[]操作符可以实现对对象内容的访问和更改。
(2) begin + end
通过begin()可以获得第一个字符的迭代器,end()可以获得最后一个字符的下一个位置的迭代器(迭代器的具体内容会在后面讲解),通过begin()和end()就可以实现对一个string对象的遍历。
(3) rbegin 和 rend
细心的同学可以发现,rbegin和rend只是在begin和end前面多加了一个r,也就是说,rbegin和rend是获取逆向的迭代器。rbegin()是获取最后一个字符的迭代器,rend()是获得第一个字符的前一个位置的迭代器。
上图就是逆向迭代器的用法。
(4)三种遍历方式
通过上面三种类对象的访问方式的学习,我们在这里可以列举出三种对于类对象的遍历方式:
第一种: for + operator[]
for(int i = 0; i < (int)s.size(); ++i)
cout << s[i] << endl;
第二种:迭代器
string::iterator it = s.begin();
while(it != s.end())
{
cout << *it << endl;
++it;
{
string::reverse_iterator rit = s.rbegin();
while(rit != s.rend())
cout << *rit << endl;
第三种:范围for
这个是我们在前面讲过的auto关键字。
string s("Hello World!");
for(auto c : s)
cout << c << endl;
3、string类对象的修改操作
(1) push_back()函数
push_back()函数的作用是在字符串后面尾插字符c。
int main()
{
string s1("Hello World!");
cout << s1 << endl;
s1.push_back('b');
cout << s1 << endl;
return 0;
}
上面代码中,s1被初始化为"Hello World!"字符串,然后通过push_back()函数在s1后面尾插一个字符b。
注意:push_back()函数的参数不能是字符串,只能是字符。
(2) 重载+=运算符
如果想要在类对象的后面继续连接字符串,就需要用到重载的+=运算符。作用是在字符串后面追加字符串str。
int main()
{
string s1("Hello World!");
cout << s1 << endl;
s1 += "abc";
cout << s1 << endl;
return 0;
}
可以看出初始化为"Hello World!“的s1对象用”+=“连接了"abc”,可以完成字符串的追加操作。
(4) c_str()函数
c_str函数的作用是返回C格式的字符串,也就是返回char类型的字符串。
虽然string类为了和C格式的字符串尽量相似,在字符串的末尾也加上了’ \0 ',但是与C格式的类型还是不同的,因此,这个接口专门用来返回char格式的字符串。
int main()
{
string s1("hello world!");
cout << "s1的类型为:" << typeid(s1).name() << endl;
cout << "s1.c_str()返回的类型为" << typeid(s1.c_str()).name() << endl;
return 0;
}
在上面的代码中,我们定义了一个string类型的对象s1,通过c_str()函数得到它的C格式的字符串,通过typeid运算符(typeid运算符用来获取一个表达式的类型信息),可以看出,s1是string类的,而c_str()函数返回的则是char const 类型的。
(5) find()函数
find()函数的声明如下:
find()函数通常有两个参数,一个是要查找的字符或者字符串,字符的类型是char,字符串的类型是char或者string;另一个是要查找的位置,查找的位置可以自己给出,如果没有给出的话,有一个默认的缺省值0。
find()函数的返回值是要查找的内容第一次出现的位置,如果没有找到对应的内容,则会返回npos(npos是string类定义的一个静态变量,值为-1,但由于npos是无符号整型的类型,因此没有找到的返回值为无符号整型的最大值)。
string s1("hello world! good morning!");
size_t pos = s1.find("hello");//不给pos参数,默认为0
cout << pos << endl;
pos = s1.find("hello", 3);
cout << pos << endl;
分别从第0个位置和第3个位置找字符串"hello",得到的结果,一个返回的是"hello"第1次出现的位置下标0,另一个则返回的是无符号整型的最大值4294967295。
(6)rfind()函数
rfind()函数与find函数的功能是相似的,不同的是rfind()函数是从字符串的末尾开始找,返回第一次找到的位置。
5、string类的非成员函数
(1)重载>>运算符和重载<<运算符
对于一个string类的对象,如果用标准库中的<<和>>运算符是没有办法直接将string类对象的内容打印出来的。
如上面两张图所示,标准库中的<<和>>运算符只重载了比较基本的数据类型,并没有将string类的输入和输出进行重载,因此我们想要直接打印string类对象的内容,就需要在string类中重载<<和>>运算符。
(2) getline函数
假如想在string类的对象中输入一串字符串,我们用标准输入来试一下:
string s1;
cin >> s1;
cout << s1 << endl;
然而得到的结果是这样的:
想要对string类对象输入hello world,结果只输入进去hello,这不是我们想要的结果。
出现这种情况是因为,cin在输入字符串的时候,识别出空格或者换行就会默认为输入结束,不管后面还有多少的东西。
对于这种情况,就要用getline()函数来进行输入。
getline()函数是非成员函数,因此调用时直接调用即可,其声明如下:
第一个参数是标准输入流,用来进行输入操作;第二个参数是要输入的string对象。
用法如下:
string s1;
getline(cin, s1);
cout << s1 << endl;
这次输入保留了输入过程中的空格,可以读取一整行数据。
但是注意:geiline()函数遇到换行符就读取结束,也就是说getline读取的也只能是一行数据