标准库中的string类
首先关于string类的了解,我先给出官方的string类的讲解,以便于大家的学习:链接: http://www.cplusplus.com/reference/string/string/?kw=string
这个网站是C++官方网站,里面对于各个关键字和库函数的讲解都是很官方的,大家有需要的时候可以参考
我们对string类进行一个简单的总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字节或者变长字符的序列。
这里有一个需要注意的点:
在使用string类时,必须包含#include头文件以及using namespace std;
下面我们就对string类的一些常用的接口进行讲解:
string类的常用接口说明
string类对象的常见构造
首先我们看看创建一个空的string类,就是一个空的字符串:
#include<iostream>
using namespace std;
int main()
{
string s1;
cout << s1 << endl;
return 0;
}
输出为空:
构造string类对象:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1 << endl;
return 0;
}
拷贝构造:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1 << endl;
string s2(s1);
cout << s2 << endl;
return 0;
}
将n个字符放入一个string类中:
#include<iostream>
using namespace std;
int main()
{
string s1(5,'a');
cout << s1 << endl;
return 0;
}
去一个string类中的字符存入另一个string类:
例如,将s1中第六个字符开始往后取三个字符放入s2
这里注意:
如果这里的要取字符的数量大于字符串的字符个数的话,能取多少取多少
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1 << endl;
string s2(s1, 6, 3);
cout << s2 << endl;
return 0;
}
string类对象的容量操作
我们这里的size和lenth的作用是一样的,只是在后期的语言发展中为了适应需要,例如,如果是一个二叉树,用lenth合适吗,所以就引出了size
注意,这里计算出来的是有效字符个数,也就是说不包括’\0’
用法如下:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.length() << endl;
cout << s1.size() << endl;
return 0;
}
capacity是返回空间的大小:
至于容量为什么是15,这就是底层实现的原因了,一些编译器是基于1.5倍的扩容,比如我们的vscode,而linux的终端里是以2倍的扩容,其实不必太过于纠结这一点,我们只需要了解到这一点即可
但是各自的容量起点也不一样!
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.capacity() << endl;
return 0;
}
empty函数的用法很简单:
例如,不为空返回0
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.capacity() << endl;
cout << s1.empty() << endl;
return 0;
}
clear的作用就是清空字符串:
清理之后输出空行,字符串被清空
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1 << endl;
s1.clear();
cout << s1 << endl;
return 0;
}
这里有一个重点内容:
reserve和resize的区别
其实二者都是用来提前开空间的,因为扩容会影响程序的效率
例如:
可以看到二者都成功地开辟了空间
并且使用resize时还可以填充多余的空间:
但其实这不是重点,重点在于:
这是resize:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.capacity() << endl;
cout << s1.size() << endl;
s1.resize(30);
cout << s1.capacity() << endl;
cout << s1.size() << endl;
return 0;
}
这是reserve:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.capacity() << endl;
cout << s1.size() << endl;
s1.reserve(30);
cout << s1.capacity() << endl;
cout << s1.size() << endl;
return 0;
}
大家可以发现:
用reserve来提前开辟空间,capacity改变,size不变,而用resize则是capacity和size都会发生改变!
下面给大家一个小的总结:
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。 - clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
string类对象的访问及遍历操作
operator[],就是返回该位置的字符,其实就是用于循环:
但是要注意一点,如果访问越界,就会直接报错,导致程序终止
并且该函数一般只适用于数组结构
#include<iostream>
using namespace std;
int main()
{
string const s1("hello world");
for (int i = 0; i < s1.length(); i++)
{
cout << s1[i];
}
cout << endl;
return 0;
}
迭代器:
迭代器有两种,还有一个叫做反向迭代器,就是从尾部开始遍历
在这里我们暂且把begin认为是指针来理解
我们使用正向的迭代器的时候要引出string类里面的iterator
begin就默认是从零开始,输出记得用解引用,指针向后移动
我们给出一张对比图:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
for (string::iterator i = s1.begin(); i != s1.end(); i++)
{
cout << *i;
}
return 0;
}
而反向迭代器就是从后开始往前移动:
这里要引出的是 reverse_iterator 其实这里的rbegin已经不在字符串的第一个位置了二者是有区别的,并且,这里的指针也是++,不然怎么会叫做反向迭代器呢?
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
for (string::reverse_iterator i = s1.rbegin(); i != s1.rend(); i++)
{
cout << *i;
}
return 0;
}
另外大家记住一个点:
for循环的底层实现就是迭代器!
string类对象的修改操作
尾插字符:
尾插很简单
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
s1.push_back('!');
cout << s1 << endl;
return 0;
}
追加字符串:
相比较push_back,append只能追加字符串,而前者只能追加字符
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
s1.append(" hhhh!");
cout << s1 << endl;
return 0;
}
但是operator+=能够把上面两个函数一起实现,并且函数的可读性更高,所以我们一般选择使用+=来实现对对象的追加:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
s1+=(" hhhh!");
cout << s1 << endl;
s1 += ('x');
cout << s1 << endl;
return 0;
}
返回C字符串:
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.c_str() << endl;
return 0;
}
返回字符串中的位置:
返回位置既可以从前往后找又可以从后往前找
find是从前往后,rfind是从后往前,npos是字符串的长度
如果找不到的话这里的npos是-1:
但是find函数返回的是无符号整形,所以-1进行了转换
截取字符:
从pos位置开始往后截取n个字符
如果n大于length-pos,能截取多少就截取多少
#include<iostream>
using namespace std;
int main()
{
string s1("hello world");
cout << s1.substr(3, 4) << endl;
cout << s1.substr(3, 12) << endl;
}
下面我们对于string类的修改操作做一个小总结:
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
string类非成员函数
其实这里用的不多,不做过多的讲解
但是这个getline函数是可以用到一些题目中来读取字符串的,他遇到换行符就会停止读取,遇到空格不会:
int main()
{
std::string name;
std::cout << "Please, enter your full name: ";
std::getline(std::cin, name);
std::cout << "Hello, " << name << "!\n";
return 0;
}
好了,今天的分享到这里就结束了,谢谢大家的支持!