【编码艺术:掌握String类函数接口的妙用指南】(二)

简介: 【编码艺术:掌握String类函数接口的妙用指南】

【编码艺术:掌握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

相关文章
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
58 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
32 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
56 4
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
25 2
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
20 0
|
2月前
|
C++ 容器
C++入门7——string类的使用-2
C++入门7——string类的使用-2
21 0
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
40 0
java基础(13)String类
|
4月前
|
API 索引
String类下常用API
String类下常用API
44 1
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①