【C++杂货铺】string使用指南(二)

简介: 【C++杂货铺】string使用指南(二)

📖shrink_to_fit()

将capacity容量缩至合适,一般不会缩小到和size一样大,可能会比size大一点。

int main()
{
  string s2("Hello C++!");
  cout << "最初的s2.size():" << s2.size() << endl;
  cout << "最初的s2.capacity():" << s2.capacity() << endl;
  s2.reserve(100);//扩容
  cout << "reserve(100)后的s2.size():" << s2.size() << endl;
  cout << "reserve(100)后的s2.capacity():" << s2.capacity() << endl;
  s2.shrink_to_fit();//缩容
  cout << "缩容后的s2.size():" << s2.size() << endl;
  cout << "缩容后的s2.capacity():" << s2.capacity() << endl;
  return 0;
}

41985da2fd4e475fa8c64ead1ad46db3.png

2.3 与对象访问及遍历有关的操作

📖operator[ ]

d5656054b4f8465a8abe93935173400a.png

int main()
{
  string s1("Hello C++!");//普通对象
  for (size_t i = 0; i < s1.size(); i++)
  {
    cout << s1[i];
  }
  cout << endl << "修改后:";
  for (size_t i = 0; i < s1.size(); i++)
  {
    cout << ++s1[i];//因为返回值是引用所以可以用[]对其进行修改
  }
  cout << endl;
  const string s2("Hello World!");
  for (size_t i = 0; i < s2.size(); i++)
  {
    cout << s2[i];
  }
  cout << endl;
  return 0;
}

2c8fd92e30ba4054896ffa034aed76da.png

小Tips:因为operator[]的返回值是一个引用,所以可以通过[]加下标的方式去访问和修改string类对象,和内置类型的数组不同,这里的s1[i]本质上是去调用函数,而内置类型的数组使用[]本质是解引用。如果发生越界访问,程序会直接报错,at接口和operator[]接口的功能类似,只不过at接口在发生越界访问的时候会抛出异常。

📖迭代器

迭代器是一种抽象的设计概念,它提供一种方法,使之能够依序巡访某个容器所含的各种元素,而又无需暴露该容器的内部表述方式。迭代器是一种行为类似指针的对象,它是容器与算法的桥梁,算法需要去访问容器中的数据,但是容器的数据都是私有的,并且有多种容器,针对不同的容器,某一算法的具体实现可能不同,例如对链表逆置和对顺序表逆置,具体过程当然是不同的,但是迭代器的出现却将它们统一了起来,对于一个算法,无论是什么容器,只需要将它的迭代器区间传过来即可,算法只使用统一的逻辑。因此任何容器的迭代器类型都用iterator来表示,它是一个类的内置类型,通过typedef得到,关于迭代器更具体地内容我将在后续模拟实现的文章中为给大家分享,今天我们只需要知道如何使用即可。

📖begin、end

03066e9715a642d8a88c407b680f9771.png


dc85d540714a4836a1a62b42099d6a29.png

int main()
{
  string s1("Hello C++!");//普通对象
  string::iterator it = s1.begin();
  while (it < s1.end())
  {
    cout << *it;//解引用迭代器
    it++;//++迭代器,让迭代器向后走
  }
  cout << endl;
  it = s1.begin();
  while (it < s1.end())
  {
    ++(*it);//通过迭代器去修改
    it++;
  }
  it = s1.begin();
  while (it < s1.end())
  {
    cout << *it;
    it++;
  }
  cout << endl;
  return 0;
}

36b9e8acc25a4f65ac9a9b3bd4d19528.png

从上面的代码可以看出,一个迭代器对象和一个指针类型的变量十分相似,都可以通过*解引用,并且都可以++,还可以解引用后去修改。但本质上对迭代器的这些操作都是通过运算符重载来实现的,具体实现我将在后续文章中为大家介绍。

小Tips:迭代器区间永远都是左闭右开,迭代器类型作为类的内置类型可以直接通过类名::iterator访问,例如:string::iterator就表示string类里面的迭代器类型。普通迭代器可读可写,const迭代器限制的是其指向的内容,只能读不能写,而const迭代器本身可以修改。

📖rbegin、rend

1a6201964a734c0fa45d9d4f8e97a71b.png

5d82789399574e828495888dd391f4d5.png

int main()
{
  string s1("Hello C++!");//普通对象
  string::reverse_iterator it = s1.rbegin();
  while (it < s1.rend())
  {
    cout << *it;//解引用迭代器
    it++;//++迭代器,让迭代器向后走
  }
  cout << endl;
  return 0;
}

f2199ba9e11f4e8fb4cfbc251df64bd8.png

小Tips:reverse_iterator一般被叫做反向迭代器,因为它可以倒着去遍历。

📖范围for

int main()
{
  string s1("Hello C++!");//普通对象
  for (auto it : s1)
  {
    cout << it;
  }
  cout << endl;
  for (auto& it : s1)//用引用就可以进行修改
  {
    it--;
  }
  for (auto it : s1)
  {
    cout << it;
  }
  cout << endl;
  return 0;
}

4119d1aba87c4f499df255aca5935df8.png

小Tips:范围for就是基于迭代器实现的,在底层范围for会转化成正向迭代器。换言之,一个容器如果不支持迭代器,那它必定也不支持范围for。

2.4 与对象修改有关的操作

📖operator=

赋值运算符重载是string类的一个默认成员函数,该函数有三个重载形式,如下图所示:

aec318e6f5ad449380ee8bacc6c555a1.png

int main()
{
  string s1("Hello C++!");
  string s2("你好,C++!");
  cout << s2 << endl;//原始的s2
  s2 = s1;//string类对象
  cout << s2 << endl;//第一次赋值后的s2
  s2 = "春人.";
  cout << s2 << endl;//第二次赋值后的s2
  s2 = 'a';
  cout << s2 << endl;//第三次赋值后的s2
  return 0;
}

ef484a71583544d7a3ce217c85bebde4.png

📖void push_back (char c)

将一个字符c追加到string类对象的末尾,它的长度增加1。

int main()
{
  string s1("Hello C++!");
  cout << "追加前:" << s1 << endl;
  s1.push_back('s');
  cout << "追加后:" << s1 << endl;
  return 0;
}

ce2c3c830e8b42a4a721c4b7feeb9943.png

📖append

append是在源字符串的后面进行追加操作的成员函数,它有七种重载实现形式,如下图所示:

cfacb0159c264da4a72dcf09fe95e248.png

int main()
{
  string s1("Hello C++!");
  string s2("aaaa");
  cout << "追加前:" << s2 << endl;
  s2.append(s1);
  cout << "追加一个string对象:" << s2 << endl;
  s2 = "aaaa";
  s2.append(s1, 6, 3);
  cout << "追加一个string对象的一部分:" << s2 << endl;
  s2 = "aaaa";
  s2.append("你好");
  cout << "追加一个C类型的字符串:" << s2 << endl;
  s2 = "aaaa";
  s2.append("Hello!", 2);
  cout << "追加一个C类型字符串的前两个字符:" << s2 << endl;
  s2 = "aaaa";
  s2.append(5, 'b');
  cout << "追加五个字符b:" << s2 << endl;
  s2 = "aaaa";
  s2.append(s1.begin()+2, s1.begin()+4);
  cout << "追加一个迭代器区间:" << s2 << endl;
  return 0;
}

fbdcff7abaf84deb8adf6a48f62512a6.png

📖operator+=

通过重载运算符+=实现追加,该运算符重载有三种重载实现形式,如下图所示:

5a960c7c82164ef8ade1bff9c148edc1.png

int main()
{
  string s1("Hello C++!");
  string s2("aaaa");
  cout << "追加前:" << s2 << endl;
  s2 += s1;
  cout << "追加一个string类对象:" << s2 << endl;
  s2 = "aaaa";
  s2 += "bcde";
  cout << "追加一个C类型的字符串:" << s2 << endl;
  s2 = "aaaa";
  s2 += 'o';
  cout << "追加一个字符:" << s2 << endl;
  return 0;
}

a28bc1ee50304fd7b32f4898708dd789.png

小Tips:除了上面介绍的一些常用的字符串修改接口外,还有一些不太常用的,例如:assign(内容替换)、insert(指定位置插入)、erase(删除)、replace(部分替换)、swap(交换两个字符串)。它们的使用方法都大同小异。

2.5 与查找有关的接口

📖c_str()

该接口的返回值类型是const char*,即返回一个C格式的字符串,该接口起到桥梁作用。

int main()
{
  string s2("Hello C++!");
  const char* str = s2.c_str();
  cout << str << endl;
  return 0;
}

ee55de30eda541a5b153a8e9683450a2.png

📖find

从字符串的pos位置开始往后查找字符或字符串,返回其在当前字符串中的位置。

9ac6e3ab53b842639b5c027e430c8572.png

int main()
{
  string s1("https://www.csdn.net/?spm=1011.2124.3001.4476");
  size_t pos1 = s1.find("csdn");
  cout << pos1 << endl;
  size_t pos2 = s1.find("www.csdn.net", 7, 3);
  cout << pos2 << endl;
  return 0;
}

0506b6e61a634c50b952d142b97879eb.png

小Tips:一般在没有找到的情况下会返回npos,即整型最大值。

📖rfind

在字符串pos位置开始往前查找字符或字符串,返回其在当前字符串中的位置。

975ec97e319a41beabe2613c874828c3.png

int main()
{
    std::string str("The sixth sick sheik's sixth sheep's sick.");
    std::string key("sixth");
    std::size_t found = str.rfind(key);
    if (found != std::string::npos)
        str.replace(found, key.length(), "seventh");
    std::cout << str << '\n';
    return 0;
}

dd1784340f164cd7bfbdd462da40f046.png

📖substr (size_t pos = 0, size_t len = npos)

在源字符串中,从pos位置开始,截取n个字符,以string的形式返回。

int main()
{
  string s1("https://www.csdn.net/?spm=1011.2124.3001.4476");
  size_t pos1 = s1.find("csdn");
  string s2 = s1.substr(pos1, 8);
  cout << s2 << endl;
  return 0;
}

3f9b3c63258441b9ac30515d505104cd.png

小Tips:除了上面介绍的一些常用接口,还有一些不常用的,比如:find_first_of(在字符串中搜索与其参数中指定的任何字符匹配的第一个字符)、find_last_of(查找最后一个匹配的)、find_first_not_of(查找第一个不匹配的)、find_last_not_of(查找最后一个不匹配的)。

2.6 string类的非成员函数

有些运算符重载函数存在竞争左操作数的问题,所以它们写在string类的外面,是类的非成员函数,主要有下面几种:

函数 功能说明
operator+ 尽量少用,因为是传值返回,会进行深拷贝导致效率降低
operator>> 输入运算符重载
operator<< 输出运算符重载
relational operators 大小比较运算符重载
getline 获取一行字符串

小Tips:operator>>和getline的区别在于,前者遇到空格' '和换行\n会截止,而后者默认只有遇到换行\n才截止,因此当我们需要从键盘读取一个含有空格的字符串是,只能用getline。

2.7 与类型转换有关的接口

📖string类型转成其他内置类型

c96bd1091c1a40138b982e842243183f.png

📖to_string将内置类型转成string类型

27905c0b67f6429da5d3daf6d3960ceb.png

# 三、编码问题

我们平时使用的 string 本质上是通过对类模板 basic_string 用字符型 char 实例化得到的。

01f6501d6ae44a0ca4e337f11a493b57.png

搞成模板的原因是为了兼容其他字符类型字符串的管理。即字符不只有 char 类型,还有 wchar_t 、char16_t、char32_t等,它们的区别在于存储一个字符所需要的字节数不同。这些不同类型的字符本质上和编码有关,我们目前C/C++中接触最多的是 ASCII 编码,它是用一个字节来存储一个字符。编码产生的本质是,计算机底层只认识二进制01序列,并不认识这些字符,因此要将字符存到计算机里面就需要建立一个字符与二进制01的对应关系,这个这关关系也被叫做映射表,也就是我们所说的编码表,而 ASCII 表是老美搞出来表示它们国家常用字符的映射表,大多是一些字母和符号。为了表示我们国家的文字,我们国家出台了 gbk 编码,此外国际上还有 unicode 编码(万国码)。

🎁结语:

 今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是春人前进的动力!


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