文章目录
- 1. 为什么要学习string类
- 2. 标准库的string类
- 2.1 string的构造函数
- (1)无参构造(重点)
- (2)用字符串初始化(重点)
- (3)用字符串的前n个字符初始化
- (4)拷贝构造(重点)
- (5)拷贝str的一部分
- (6)拷贝n个字符
- (7)用迭代器初始化
- 2.2 string的访问及遍历
- 1. operator[]
- 2.Iterators(迭代器遍历)
- 反向迭代器
- 3. 范围for遍历
- auto关键字
- 范围for遍历
- 2.3 string的容量操作
- 1.size和length(size是重点)
- 2. max_size
- 3. capacity
- 4. resize(重点)
- 5. reserve(重点)
- 补充
- 6. clear
- 7. empty
- 8. shrink_to_fit
- 结语
1. 为什么要学习string类
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
2. 标准库的string类
string类的文档介绍
在使用string类时,必须包含#include头文件以及using namespace std;
2.1 string的构造函数
(1)无参构造(重点)
C++为了与C语言的<string.h>
区分开,是不会加 .h 的(C++的头文件<string>
)
string();
无参构造的string内是没有元素的。
#include<iostream>
#include<string>
using namespace std;
void test_string_Construct()
{
string s1;
cout << s1 << endl;
}
注意:我们这里是使用的<<
是string类实现的全局输出函数(string类还实现了>>
输入函数)
(2)用字符串初始化(重点)
string (const char* s);
文档内容:
Copies the null-terminated character sequence (C-string) pointed by s.
将C字符串拷贝给对象。
string s2("Hello World");
(3)用字符串的前n个字符初始化
string (const char* s, size_t n);
拷贝C字符串的前n个字符到string类的对象
string s3("Hello World", 5);
(4)拷贝构造(重点)
string (const string& str);
一个str初始化一个新对象
string s2("Hello World");
string s4(s2);
(5)拷贝str的一部分
string (const string& str, size_t pos, size_t len = npos);
pos为拷贝的开始位置,len为拷贝的字符个数,npos是string的成员常量
代表如果没给拷贝的字符个数,默认拷贝到str的\0
没有给要拷贝的个数
string s5(s2, 6);
cout << s5 << endl;
给了拷贝的个数
string s6(s2, 6, 3);
cout << s6 << endl;
(6)拷贝n个字符
string (size_t n, char c);
用n个字符拷贝初始化str
string s7(5, 'x');
cout << s7 << endl;
(7)用迭代器初始化
template <class InputIterator>
string (InputIterator first, InputIterator last);
按相同顺序复制范围 [first,last] 内的字符序列。
string s2("Hello World");
string s8(s2.begin(), s2.end());
cout << s8 << endl;
2.2 string的访问及遍历
1. operator[]
返回string类中第pos位置的引用,也支持const string类调用(但权限是只读)
string s1("Hello World");
//下标+[]遍历
//普通对象
for (int i = 0; i != s1.size(); i++)
{
s1[i] += 2;
cout << s1[i] << ' ';
}
cout << endl;
//const对象
const string s2("Hello World");
for (int i = 0; i != s1.size(); i++)
{
//s1[i] += 2;
cout << s1[i] << ' ';
}
const对象这样操作会报错(权限只有只读)
2.Iterators(迭代器遍历)
STL六大组件之一,这里只讲一些迭代器的用法,后期我们会详细讲解。
返回一个指向string第一个字符的迭代器
string s1("Hello World");
string::iterator sit = s1.begin();
分析下上面的代码,我们先突破了string类域,指定了iterator 定义了变量 sit,然后 sit 接收了来自s1.begin()的返回值,也就是指向第一个字符的位置。
从这里我们可以感觉到这个迭代器的功能特别像指针,但是!迭代器不一定是指针,因为迭代器不仅仅是数组类数据结构有,而是STL的数据结构都有迭代器的功能,像map结构,list结构他们都有迭代器,而且所有迭代器还支持++和解引用,这些迭代器都是重载过的,当然我们也可以把他当作指针用,因为它的用法和指针十分相似。
返回一个指向最后一个字符的迭代器('\0'
)
//迭代器遍历
string s1("Hello World");
string::iterator sit = s1.begin();
for (; sit != s1.end(); sit++)
{
cout << *sit << ' ';
}
cout << endl;
我们也可以使用迭代器对s1进行修改
如果你只希望迭代器进行“读”的操作,不进行“写”的操作,那就可以使用const迭代器
const迭代器,功能和迭代器一样,只是没有了“写”的权限,
string::const_iterator cit = s1.begin();
while (cit != s1.end())
{
cout << *cit << ' ';
cit++;
//(*cit)++;
}
cout << endl;
反向迭代器
顾名思义,迭代器的方向是反方向的,这也就证明了迭代器并不是指针,指针是无法通过++来往后走的。
Returns a reverse iterator pointing to the last character of the string (i.e., its reverse beginning).
返回一个反向迭代器,这个迭代器指向string的最后一个字符的前一个位置(最后是'\0'
)
Reverse iterators iterate backwards: increasing them moves them towards the beginning of the string.
反向迭代器向后迭代:增加迭代器会使其向字符串的开头移动。
Returns a reverse iterator pointing to the theoretical element preceding the first character of the string (which is considered its reverse end).
返回一个反向迭代器,指向字符串第一个字符(被视为其反向末端)之前的理论元素。
The range between string::rbegin and string::rend contains all the characters of the string (in reverse order).
rbegin和rend之间的顺序是相反的
这里还有最有一种迭代器,const反向迭代器,理解了前面的const迭代器和反向迭代器,就能知道该怎么用了,这里就不赘述了
补充:const迭代器都是C++11加入进来的。
3. 范围for遍历
C++11支持更简洁的范围for的新遍历方式
auto关键字
在这里补充2个C++11的小语法,方便我们后面的学习。
- 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。(也就是自动推导类型)
就拿迭代器来说,我们如果要写一个反向迭代器,我们需要写
string::reverse_iterator rit = s1.rbegin();
但是如果我们用了auto,那就不用写那么长的代码
auto resit = s1.rbegin();
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
范围for遍历
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
范围for可以作用到数组和容器对象上进行遍历
范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
我们也可以不通过汇编来看(错误使用范围for的e)
2.3 string的容量操作
1.size和length(size是重点)
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
string s1("hello worldxxxxxxx");
//求元素个数
cout << "size:" << s1.size() << endl;
cout << "length:" << s1.length() << endl;
2. max_size
Returns the maximum length the string can reach.
返回string能到达的最大长度
string s1("hello worldxxxxxxx");
//返回string的最大长度
cout << "max_size:" << s1.max_size() << endl;
32位环境下是21亿多
64位就更长的(达到了九十多亿万亿)
3. capacity
Returns the size of the storage space currently allocated for the string, expressed in terms of bytes.
返回当前空间所分配的大小(以字节为单位)
这个我们就很熟悉,也就不多讲解了
string s1("hello worldxxxxxxx");
//求容量大小
cout << "capacity:" << s1.capacity() << endl;
4. resize(重点)
Resizes the string to a length of n characters.
将size修改到n
If n is smaller than the current string length, the current value is shortened to its first n character, removing the characters beyond the nth.
如果你给的n比原来的size小,那么会将当前值缩短为前 n 个字符,并删除 n 个字符之后的字符。
If n is greater than the current string length, the current content is extended by inserting at the end as many characters as needed to reach a size of n. If c is specified, the new elements are initialized as copies of c, otherwise, they are value-initialized characters (null characters).
如果你给的n比原来的size大且指定了字符c,新元素将被初始化为 c 的副本,否则,它们将是值初始化字符(空字符)。
string s1("hello worldxxxxxxx");
cout << endl << "改变内容个数:" << endl;
//改变内容个数
s1.resize(s1.size() - 5); //小于原本的size会删除string内部的元素(从后往前删)
cout << s1 << endl;
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl << endl;//减少了size并不会减少capacity
s1.resize(s1.size() + 5); //大于原本的size,会在多余的地方加入'/0'
cout << s1 << endl;
cout << "size:" << s1.size() << endl << endl;
s1.resize(s1.size() + 2, '#'); //大于原本的size且指定了字符,会在尾部插入n个指定字符
cout << s1 << endl;
cout << "size:" << s1.size() << endl << endl;
5. reserve(重点)
reserve改变的是capacity。
Requests that the string capacity be adapted to a planned change in size to a length of up to n characters.
要求将字符串容量调整为不小于size的长度,以适应计划中的大小变化。
If n is greater than the current string capacity, the function causes the container to increase its capacity to n characters (or greater).
如果 n 大于当前字符串容量,该函数会使容器的容量增加到 n 个字符(或更多)。
In all other cases, it is taken as a non-binding request to shrink the string capacity: the container implementation is free to optimize otherwise and leave the string with a capacity greater than n.
在如果给的数字在size与capacity之间,那么缩减字符串容量的请求就不一定会执行:容器实现可以自由地进行其他优化,使字符串容量大于 n。
This function has no effect on the string length and cannot alter its content.
这个函数无法影响string的size和修改string的内容
string s1("hello");
//改变容量
s1.reserve(4);//容量比size小
cout << "capacity:" << s1.capacity() << endl; //不会缩容(这里不管vs还是g++都不会缩容)
s1.reserve(10);//改变的值在capacity与size之间
cout << "capacity:" << s1.capacity() << endl; //在vs平台下不会缩容(但是在g++里会缩容)
s1.reserve(20);//改变的值比capacity大
cout << "capacity:" << s1.capacity() << endl; //会扩容
补充
补充一个小知识点,从上面的代码我们得知,只有5个元素的string竟然开了16个空间(多出来个是给'\0'
)
这是因为vs在设计string类时,设计了两个存储空间,当capacity小于16个时,存储在有16个字节大小的_Buf数组里面,当capacit大于16时,再存储到堆上开辟的动态内存。
当我们把size或者capacity修改到大于16时,数据就会存储到_Ptr
里面。
6. clear
清空有效字符
Erases the contents of the string, which becomes an empty string (with a length of 0 characters).
清空string的内容,并将string的size置为0
string s1("hello worldxxxxxx");
//清除string的元素
s1.clear();
cout << "size:" << s1.size() << endl; //归零
cout << "capacity:" << s1.capacity() << endl; //不变
clear()只是将string中有效字符清空,不改变底层空间大小。
7. empty
这个就是判断string是否为空
string s1("hello worldxxxxxx");
//判断string是否为空
s1.clear();
cout << s1.empty() << endl;
s1 += 'a';
cout << s1.empty() << endl;
判断的是string类的size是否为0
8. shrink_to_fit
这是C++11新加入的函数
Requests the string to reduce its capacity to fit its size.
要求string的capacity减少到size的大小
The request is non-binding, and the container implementation is free to optimize otherwise and leave the string with a capacity greater than its size.
该请求是非约束性的,容器实现可以自由地进行其他优化,使字符串的容量大于其大小。(但vs会执行)
This function has no effect on the string length and cannot alter its content.
这个函数无法影响string的size,也无法改变string的内容
string s1("hello worldxxxxxx");
cout << "capacity:" << s1.capacity() << endl;
//将capacity缩减到size
s1.resize(10);
s1.shrink_to_fit();
cout << "capacity:" << s1.capacity() << endl; //缩小了
结语
如果全部讲解就会篇幅过长,所有剩下的部分会放在下半部的博客
那么这次的分享就到这里结束了~
最后感谢您能阅读完此片文章~
如果您认为这篇文章对你有帮助的话,可以用你们的手点一个免费的赞并收藏起来哟~
如果有任何建议或纠正欢迎在评论区留言~
也可以前往我的主页看更多好文哦(点击此处跳转到主页)。