【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++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
101 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
78 2
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
39 1
|
3月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
68 0
|
3月前
|
存储 编译器 程序员
深度剖析C++string(上篇)(2)
深度剖析C++string(上篇)(2)
52 0
|
13天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
54 18
|
13天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
38 13
|
13天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
37 5
|
13天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
28 5
|
13天前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
33 4