【编码艺术:掌握String类函数接口的妙用指南】(一):https://developer.aliyun.com/article/1425648
不过通常我们都使用下标 + [ ]进行逆序遍历,但是反向迭代器也是非常有用的,对于链表这种没有下标的逆序遍历就需要使用反向迭代器。对于上面的迭代器,类型名都是非常长的,而且比较容易写错,这里我们可以使用之前我们讲到的auto关键字自动推至类型,但是这个写法对代码的可读性不好。
关于迭代器这里,之前的迭代器实现了两个版本,容易混淆,于是C++11就对const对象的迭代器进行了单独的处理,在普通迭代器前面加上了字符'c'表示此时的对象是cosnt的。
4.4.范围for
#include <iostream> using namespace std; int main() { string str("Test string"); for (auto e : str) { cout << e; } cout << endl; return 0; }
范围for在我们这里不仅可以支持string,还能支持vertor,list等其他容器。我们现在再来介绍一下下面的元素存取相关函数。
我们先来看一个代码
当我们如果不小心访问了越界元素,此时程序就会报错,但是此时看右边我们知道是assert断言出错,如果我们不想出现这个报错界面,并且提示一下错误信息,就可以使用at,at也可以访问元素,它返回字符串中位置 pos 处的字符的引用,当访问越界的时候,此时提示信息"invalid string position"。
我们来验证一下。
再来看一下front和back,它们是访问字符串的头位置和尾位置的字符
int main() { string s("hello world"); cout << s.front() << endl; cout << s.back() << endl; cout << s[0] << endl; cout << s[s.size() - 1] << endl; return 0; }
运行结果:
一般情况上我们很少用frong和back,因为我们可以通过pos为0和size-1位置访问头和尾元素。
5. string类对象的容量操作
5.1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
5.2.max_size()返回字符串可以达到的最大长度,不同的机器这个最大长度是不一样的,下面的测试是32位机器下
虽然上面给了我们这个字符串能到达的最大长度,但是这个值没有参考意义,实际上我们开辟不了这个最大长度的空间,使得字符串到达的最大长度。我们来验证一下
int main() { try { string str1; string str2("Test string"); cout << "str1:" << str1.max_size() << endl; cout << "str2:" << str2.max_size() << endl; //reverse:为字符串预留空间 - 相当于扩容 //实践中没有参考的价值和意义 str1.reserve(str1.max_size()); } catch(const exception& e) { cout << e.what() << endl; } return 0; }
运行结果:
5.3.capacity()返回空间总大小
注意:这里的容量是有效字符的数量,而'\0'不属于有效字符,这里实际上开了16个字节的空间,其中一个用来存 '\0'。
我们这里还能用来检测string的扩容机制
int main() { try { string str1; size_t old = str1.capacity(); cout << "str1:" << str1.capacity() << endl; //检测string的扩容机制 for (size_t i = 0; i < 100; i++) { str1.push_back('x'); if (old != str1.capacity()) { cout << "str1:" << str1.capacity() << endl; old = str1.capacity(); } } } catch(const exception& e) { cout << e.what() << endl; } return 0; }
vs下结果:P. J. 版本 - 1.5倍扩容
Linux(g++)下结果:SGI版本 - 2倍扩容
5.4.reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间capacity总大小时,reserver不会改变容量大小。
int main() { try { string str1; str1.reserve(100); size_t old = str1.capacity(); cout << "str1:" << str1.capacity() << endl; //检测string的扩容机制 for (size_t i = 0; i < 100; i++) { str1.push_back('x'); if (old != str1.capacity()) { cout << "str1:" << str1.capacity() << endl; old = str1.capacity(); } } } catch(const exception& e) { cout << e.what() << endl; } return 0; }
reserve可以和capaciy搭配使用,当使用reverse(100)的时候,此时就已经开辟了100空间,后面就不需要扩容了。如果我们确定需要多少空间时,提前开空间即可,此后就不需要扩容了。但是这里我们只需开辟100个空间,而程序却开辟了111个空间,因为vs下可能存在了内存对齐机制,会以它内部的规则去对齐,从而会开辟更大一点,Linux下直接开辟你确定所要的空间。
vs下运行结果:
Linux下的结果:
我们再来看一下当reserve的参数小于 string的底层空间capacity总大小时,reserver不会改变容量大小。
int main() { string str2("hello world!xxxxxxxxxxx"); str2.reserve(100); cout << "str2:" << str2.capacity() << endl; str2.reserve(5); cout << "str2:" << str2.capacity() << endl; return 0; }
vs下运行结果:
Linux下的结果:
当reserve的参数小于 string的底层空间capacity总大小时,vs平台下reserver不会改变容量大小,Linux下reserver会改变容量大小,但是它缩小空间只会缩小到该字符串的有效字符数量size处,它是不会影响到已经存储的字符串的。
5.5. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用’\0'来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。
注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
reserve只影响我们的容量,不会影响数据,而resize既影响容量,也会影响数据。我们会发现s1的大小和容量都增加了,那它是用什么填充这些空间的呢?
我们先来看一下大于当前容量的情况,我们可以通过监视窗口观察到,但是'\0'不一定是标识字符,有效字符属于0到size-1范围内。
我们再来看一下大于size但是小于capacity的情况
此时容量大小没有改变,只变化了数据,仅仅变化了size。如果小于size呢?
看一下监视窗口
此时删除了数据,只保留前resize个。resize应用的场景是开辟空间并指定字符初始化。注意:C++中new对应C语言中的malloc函数,而C++中没有C语言中对应的realloc扩容函数。所以C++中,我们如果使用字符数组,就会使用string,而string中提供了相应的接口,从而进行扩容。
【编码艺术:掌握String类函数接口的妙用指南】(三):https://developer.aliyun.com/article/1425652