开始使用 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂…
所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
1 继续学习
上一篇文章我们给出了构造函数和一些成员函数的功能:
成员函数 | 作用 |
begin() | 返回字符首位置 (迭代器常用 ) |
end() | 返回字符结尾 (迭代器常用) |
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间 |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
1.1 扩容机制
上一篇文章,我们看到了string的容量是比较模糊的,为什么是15???,它的扩容规则是什么???
接下来我们来探索一下string的扩容机制:
首先我们来写一个简单的测试程序:
#include<string> #include<iostream> using namespace std; int main(){ string s1 = ""; for (int i = 0; i < 200; i++) { s1 += "c"; //检查是否需要扩容 然后打印出来 if (s1.size() == s1.capacity()) cout << "string 的空间 :" << s1.capacity() << endl; } return 0; }
来看看效果:
可以看出来VS2022 基本是以1.5 倍扩容。那全部的编译器都是1.5 倍吗???
再让我们来liunx中,用g++ 来试试哈。
所以不同编译器的扩容机制也不同,这就是历史的原因了。根据我们所学过的顺序表,我们可以很容易想到内部扩容机制是什么样子:
- 检查 size 是否 等于 capacity
- 如果相等 扩容
- 否则直接插入尾部。
1.2 string类对象的访问及遍历操作
函数名称 | 功能说明 |
operator[] (重点) | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | rbegin获取一个字符的迭代器 + rend获取最后一个字符下一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式(底层是迭代器) |
迭代器是C++新增的内容,功能类似指针,这里不细说,只将在string中如何使用。
首先看到迭代器(iterator)功能类似指针,那必须想到遍历的作用,对不对。接下来我们来看看迭代器的遍历好不好用。
注意不同类型的迭代器需要使用命名空间限定 这里是 string :: iterator
#include<string> #include<iostream> using namespace std; int main() { string s1 = "I love you !"; string::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << " "; it++; } return 0; }
看见这熟悉的结构,while()循环 *it it++
是不是真的和指针一样。
再让我们看一下结果吧:
顺利的遍历了全部字符串。
接下来再来看一个好玩的,逆转迭代器reverse_iterator
#include<string> #include<iostream> using namespace std; int main() { string s1 = "I love you !"; string::reverse_iterator it = s1.rbegin(); while (it != s1.rend()) { cout << *it << " "; it++; } return 0; }
仔细看,依然是 it++,而且起始位置也成为了rbegin()
结尾是rend()
是不是非常有意思。
来看效果:
成功逆转打印,very good!!!
同样也可以使用基于范围的for循环
for(auto it : s1){ }
即可完成遍历操作;
1.3 string类对象的修改操作
来给一波函数:
函数名称 | 功能说明 |
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
这里面其实有许多函数重载,不过常用的就那几个,有需求请自行查看:C++库
来初步使用一下插入函数:
#include<string> #include<iostream> using namespace std; int main() { string s1 = "I love you !"; s1.push_back('c'); cout << "结尾插入字符 :" << s1 << endl; s1.append("forever"); cout << "append 结尾插入字符串 :" << s1 << endl; s1 += "xxxxx"; cout << "+= 结尾插入字符串 :" << s1 << endl; return 0; }
非常顺利的插入了:
来看效果:
1.4 其他一些成员函数
函数 | 功能说明 |
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
这些我们以后再进行详细讲解。
2 实践解决问题:
家人们,上链接!!!!
来看题目描述:
这个我们直接套用字符串操作模版即可:
这个非常好用的获取单词的办法,首先使用基于范围的for循环,然后遍历,只要不是空格,就插入到中间string类temp中,遇到空格,就把该单词存入数组中。非常好用!
然后这道题还要求我们完成排序,所以直接使用sort按要求排序即可。
最后删除结尾的数字,重新插入到s中即可。
bool compare(string a,string b){ return a.back() < b.back(); } class Solution { public: string sortSentence(string s) { string temp = ""; vector<string> res; s += " "; for(auto ch : s){ if(ch == ' '){ res.push_back(temp); temp.clear(); } else{ temp.push_back(ch); } } sort(res.begin(),res.end(),compare); s.clear(); for(auto& it : res){ it.pop_back(); s += it + ' '; } s.pop_back(); return s; } };
运行效果非常好: