【C++从练气到飞升】09---string语法指南(一)+https://developer.aliyun.com/article/1502607
如上当不写s.reserve(100);就会发生扩容,但是当写上s.reserve(100);提前开好空间就不会发生扩容,同时要注意s.reserve(10);并不会缩减空间(缩容)
- resize (重点)- - - 将有效字符的个数改成n个,多出的空间用字符c填充
void test_string2() { string s1("hello world"); cout << s1 << endl; cout << s1.size() << endl; cout << s1.capacity() << endl; //s1.resize(13, 'x'); s1.resize(20, 'x');//这里有效字符个数改成20s1本来的有效字符hello world是11个超出部分用x补充,其次size()和capacity也会随之发生改变--->size()变成20;capacity()变成31 s1.resize(5); cout << s1 << endl; cout << s1.size() << endl; cout << s1.capacity() << endl; string s2; s2.resize(10, '#'); cout << s2 << endl; cout << s2.size() << endl; cout << s2.capacity() << endl; }
- shrink_to_fit() - - - 将capacity容量缩至合适
void test_string1() { string s; s.reserve(50); s += "hello world"; cout << s.size() << endl; cout << s.capacity() << endl; s.shrink_to_fit(); cout << s.size() << endl; cout << s.capacity() << endl; }
注意:
- 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不会改变容量大小。
2.3 string类对象的访问及遍历操作
- operator[] (重点) 返回pos位置的字符,const string类对象调用
void test_string2() { string s1("hello world\n"); string s2 = "hello world";//单参数构造支持隐式类型转换 //遍历string for (size_t i = 0; i < s1.size(); i++) { //读 cout << s1[i] << " "; } cout << endl; for (size_t i = 0; i < s1.size(); i++) { //写 s1[i]++; } cout << s1 << endl;; }
- begin+ end - - - begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
void test_string3() { string s4="hello world"; string::iterator it = s4.begin(); while (it != s4.end()) { //读 cout << *it << " "; it++; } cout << endl; it = s4.begin(); //while (it < s4.end())可以这样写但是不建议 while (it != s4.end()) { //写 *it='a'; cout << *it << " "; it++; } cout << endl; }
- rbegin + rend - - - begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
void test_string4() { string s5 = "hello world"; string::reverse_iterator rit = s5.rbegin(); while (rit != s5.rend()) { cout << *rit << " "; ++rit; } cout << endl; }
- 范围for - - - C++11支持更简洁的范围for的新遍历方式
void test_string5() { string s6 = "hello world"; //原理:编译器替换成迭代器 for (auto ch : s6) { //读 cout << ch << " "; } cout << endl; //对于写---范围for 本质自动遍历是*it赋值给ch,ch是*it的拷贝所以要写的话要加&,这样ch就是*it的别名 for (auto ch : s6)//错误写法 for (auto& ch : s6) { //写 ch++; } cout << s6 << " "; cout << endl; }
void func(const string s)//不推荐传值传参,会进行拷贝调用拷贝构造string的底层不能用浅拷贝所以用引用+const void func(const string& s) { //迭代器支持读写,但是这里是const不支持迭代器写所以C++设计出了cbegin() cend() crbegin() crend()也可以+const例如下面一行注释代码 //string::const_iterator it = s.begin(); //对比于上面代码+const和不+const还用修改可以直接使用auto直接推出类型 auto it = s.begin(); while (it != s.end()) { //读 cout << *it << " "; it++; } cout << endl; //string::const_reverse_iterator rit = s.rbegin(); auto rit = s.rbegin(); while (rit != s.rend()) { cout << *rit << " "; ++rit; } cout << endl; } void test_string6() { string s7 = "hello world"; func(s7); }
2.4 string类对象的修改操作
- push_back - - - 在字符串后尾插字符c
void test_string3() { string s; string ss("hello"); s.push_back('#'); cout <<s<< endl; }
- append - - - 在字符串后追加一个字符串
void test_string3() { string s; string ss("hello"); s.append("hello world"); s.append(ss); cout <<s<< endl; }
- operator+= (重点) - - - 在字符串后追加字符串str
void test_string3() { string s; string ss("hello"); s += '#'; s += "hello"; s += ss; cout << s << endl; }
- c_str(重点) - - - 返回C格式字符串
void test_string9() { string filename; cin >> filename; FILE* fout = fopen(filename.c_str(), "r"); }
- find + npos(重点) - - - 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
void test_string2() { string s1("test.cpp");//读取文件后缀 size_t i = s1.find('.'); string s3 = s1.substr(i); cout << s3 << endl; }
- rfind - - - 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
void test_string2() { string s2("test.cpp.tar.zip");//找文件后缀.zip size_t j = s2.rfind('.');//倒着找 string s4 = s2.substr(j); cout << s4 << endl; }
- substr - - - 在str中从pos位置开始,截取n个字符,然后将其返回
void test_string2() { string s1("test.cpp");//读取文件后缀 size_t i = s1.find('.'); string s3 = s1.substr(i); cout << s3 << endl; }
注意:
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
2.5 string类非成员函数
- operator+ - - - 尽量少用,因为传值返回,导致深拷贝效率低
void test_string3()//at失败后会抛异常 { string ss("hello"); string ret=ss + '#';//+是一个传值返回,代价比较大 string ret2 = ss + "hello"; cout << ret << endl; cout << ret2 << endl; cout << endl; }
- operator>> (重点)- - - 输入运算符重载
- operator<< (重点) - - - 输出运算符重载
- getline (重点) - - - 获取一行字符串
🌟cin>> 和getline的区别在于:>>遇到空格’ '和换行\n会截止,而getline默认只有遇到换行\n才截止,因此当我们需要从键盘读取一个含有空格的字符串是,只能用getline
void test_string3() { string s1; getline(cin, s1, '!'); cout << s1; }
- relational operators (重点)- - - 大小比较
2.6 string类类型转换接口
- string类型转换成内置类型
内置类型转换成string
2.7 vs和g++下string结构的说明
注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节
vs下string的结构
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字
符串的存储空间:
当字符串长度小于16时,使用内部固定的字符数组来存放
当字符串长度大于等于16时,从堆上开辟空间
union _Bxty { // storage for small buffer or pointer to larger one value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // to permit aliasing } _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指
针将来指向一块堆空间,内部包含了如下字段:
- 空间总大小
- 字符串有效长度
- 引用计数
struct _Rep_base { size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount; };
- 指向堆空间的指针,用来存储字符串。