【038】解码C++ STL:探索string容器的无限可能与鲜为人知的技巧

解码C++ STL:探索string容器的无限可能与鲜为人知的技巧

  • 引言
  • 一、STL概述
    • 1.1、STL的基本概念
    • 1.2、STL的六大组件
  • 二、string类
    • 2.1、string容器基本概念
    • 2.2、string容器常用操作
      • 2.2.1、string构造函数
      • 2.2.2、string基本赋值操作
      • 2.2.3、string存取字符操作原型
      • 2.2.4、string拼接操作
      • 2.2.5、string查找和替换
      • 2.2.6、string比较操作
      • 2.2.7、string提取字串操作
      • 2.2.8、string插入和删除操作
      • 2.2.9、string和c语言风格字符串转换
    • 2.3、使用示例
  • 总结

引言


作者简介:一个热爱分享高性能服务器后台开发知识的博主,目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。技能涵盖了多个领域,包括C/C++、Linux、Nginx、MySQL、Redis、fastdfs、kafka、Docker、TCP/IP、协程、DPDK等。

️ CSDN实力新星、CSDN博客专家

专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。

专栏地址:C++从零开始到精通

博客主页:https://blog.csdn.net/Long_xu


上一篇:【037】从异常到优雅的错误处理:揭秘C++异常机制的神奇力量

一、STL概述

C++ STL(Standard Template Library)是C++标准库的一部分,它提供了一套丰富而强大的模板类和函数,用于处理常见的数据结构和算法问题。STL的设计目标是提供高效、通用、可重用的组件,以支持现代C++编程。

STL包含了一系列容器、迭代器、算法和函数对象等组件,每个组件都有明确定义的功能和接口。其中,容器用于存储和管理数据,迭代器用于访问容器中的元素,算法用于对容器进行各种操作,函数对象则用于定义可调用对象,以在算法中进行自定义操作。

STL的容器分为序列式容器和关联式容器两大类别。序列式容器包括vector、list、deque等,它们按顺序存储元素并支持快速随机访问。关联式容器包括set、map、multiset、multimap等,它们根据键值进行排序和检索。

STL的迭代器是一种智能指针,提供了统一的接口访问容器中的元素。迭代器可分为输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器五种类型,每种类型具有不同的功能和限制。

STL的算法包括对容器进行排序、查找、复制、转换等操作的函数模板。它们具有良好的可重用性和高效性能,并且可以适用于不同类型的容器和数据。

除了容器、迭代器和算法,STL还提供了其他功能,如函数对象、适配器、分配器等。函数对象是可调用对象的抽象,可以在算法中以类似函数的方式使用。适配器用于改变组件的接口或行为,如stack、queue等。分配器则负责内存的分配和释放。

1.1、STL的基本概念

C++ STL(Standard Template Library)是C++标准库的一部分,它基于模板技术提供了一套通用的数据结构和算法组件,可以方便地处理各种数据操作和算法需求。它是由惠普实验室开发的一系列软件的统称,在引入C++之前该技术已经存在很长时间了。

STL从广义上分为:容器、迭代器和算法,容器和算法之间通过迭代器进行无缝连接。C++标准程序库中隶属于STL的占到80%以上。

1.2、STL的六大组件

  1. 容器(Containers):STL提供了多种容器,如vector、list、deque、set、map等。容器用于存储和管理数据,每种容器都有不同的特性和适用场景。

  2. 迭代器(Iterators):迭代器是一种抽象的概念,用于遍历容器中的元素。STL的迭代器提供了统一的接口,使得可以以相同的方式访问不同类型的容器。

  3. 算法(Algorithms):STL提供了多种算法,如排序、查找、遍历、变换等。这些算法可以直接应用于容器,并且具有高效的执行性能。

  4. 函数对象(Function Objects):函数对象是可调用对象的抽象,可以像函数一样使用。STL中的算法通常使用函数对象作为参数,以完成自定义的操作。

  5. 适配器(Adapters):适配器用于改变组件的接口或行为。例如,STL提供了stack和queue这样的适配器,用于在已有容器的基础上实现堆栈和队列的功能。

  6. 分配器(Allocators):分配器负责管理容器的内存分配和释放。STL允许在创建容器时指定自定义的分配器,以满足特定的内存需求。

STL的设计目标是提供可重用、高效的组件,使得开发者能够更加方便地处理数据结构和算法问题。通过使用STL,可以减少开发时间,提高代码的可读性和可维护性。

STL的一个重要特性是将数据和操作分离。数据由容器类别加以管理,操作则是由特定的算法完成。

不同的容器的内存结构是不一样的、数据类型是不一样的,因此,每个容器都会有一个迭代器与之一一对应,因为只有与之对应的迭代器才知道对该容器做如何操作,所以可以把迭代器看成容器的一个子变量。

【038】解码C++ STL:探索string容器的无限可能与鲜为人知的技巧_第1张图片
算法分为质变算法和非质变算法。

  • 质变算法:指运算过程中会更改区间内的元素的内容。例如拷贝、替换、删除等。
  • 非质变算法:指运算过程中不会更改区间内的元素的内容,。例如查找、计数、遍历、寻找极值等。

二、string类

2.1、string容器基本概念

C语言风格字符串(以空字符\0结尾的字符数组)太过于复杂难以掌握,不适合大程序开发,所以C++标准库定义了一种string类,定义在头文件

string和C语言风格字符串对比:

  • char* 是一个指针,string是一个类。
  • string封装了char *,管理这个字符串,是一个char型的容器。

string封装了很多实用的成员方法,例如查找(find)、拷贝(copy)、删除(delete)、替换(replace)、插入(insert)等。

string管理char* 所分配的内存。每次string的复制、取值都由string类负责维护,不用担心复制越界和取值越界等。

2.2、string容器常用操作

2.2.1、string构造函数

在C++中,string容器类的构造函数有多种重载形式,可以根据不同的需求进行使用。

  1. 默认构造函数:创建一个空字符串对象。

    string();
    
  2. 带初始值的构造函数:使用给定的字符数组、字符串对象或子串来创建字符串对象。

    string(const char* str);
    string(const string& str);
    string(const string& str, size_t pos, size_t len = npos);
    
  3. 字符个数和重复字符构造函数:创建包含指定数量重复字符的字符串对象。

    string(size_t n, char c);
    
  4. 范围构造函数:使用迭代器指定的范围内的字符来创建字符串对象。

    template<class InputIterator>
    string(InputIterator first, InputIterator last);
    
  5. 移动构造函数(C++11及以上版本):使用移动操作创建新的字符串对象,通过转移源字符串对象的资源来避免不必要的复制。

    string(string&& str) noexcept;
    

这里仅展示了部分常用形式,实际上string类提供了更多的构造函数重载,以满足不同使用场景下的需求。

2.2.2、string基本赋值操作

在C++中,string容器类提供了多种基本赋值操作,用于对字符串进行赋值。

  1. 字符串赋值:

    string& operator=(const char* str);
    string& operator=(const string& str);
    

    将一个字符数组或另一个字符串对象的内容赋值给当前字符串对象。

  2. 字符赋值:

    string& operator=(char c);
    

    将一个字符赋值给当前字符串对象,构建一个只包含该字符的字符串。

  3. 移动赋值(C++11及以上版本):

    string& operator=(string&& str) noexcept;
    

    使用移动操作将源字符串对象的内容赋值给当前字符串对象,避免不必要的复制。

  4. 子串赋值:

    string& assign(const string& str, size_t pos, size_t len = npos);
    

    将源字符串的子串赋值给当前字符串对象,从指定位置开始,并可选择指定长度。

  5. 字符重复赋值:

    string& assign(size_t n, char c);
    

    将指定数量的重复字符赋值给当前字符串对象。

  6. 范围赋值:

    template<class InputIterator>
    string& assign(InputIterator first, InputIterator last);
    

    使用迭代器指定的范围内的字符赋值给当前字符串对象。

2.2.3、string存取字符操作原型

在C++中,string容器类提供了一些常用的操作来存取字符串中的字符。注意:[]越界不会抛出异常,而at()函数越界会抛出异常。

  1. 通过下标访问字符:

    char& operator[](size_t pos);
    const char& operator[](size_t pos) const;
    

    返回字符串中指定位置的字符,可以通过下标直接进行访问和修改。

  2. 使用at()函数访问字符:

    reference at(size_type pos);
    const_reference at(size_type pos) const;
    

    返回字符串中指定位置的字符,可以通过at()函数进行访问和修改。与下标访问不同的是,at()函数会进行范围检查,并在访问越界时抛出异常。

  3. 获取第一个字符:

    char& front();
    const char& front() const;
    

    返回字符串的第一个字符。

  4. 获取最后一个字符:

    char& back();
    const char& back() const;
    

    返回字符串的最后一个字符。

  5. 获取字符数组指针:

    const char* c_str() const;
    const char* data() const;
    

    返回一个指向以空字符结尾的字符数组的指针,该字符数组包含了当前字符串对象的内容。

这些字符存取操作可以根据需要选择合适的方式来访问和修改string对象中的字符。需要注意的是,在对字符进行存取和修改时,要确保操作不会越界,并且根据需要选择适当的存取方式,以保证代码的正确性和安全性。

2.2.4、string拼接操作

在C++中,string容器类提供了多种常用的操作来进行字符串的拼接。以下是一些常见的string拼接操作原型:

  1. 使用+运算符进行字符串拼接:

    string operator+(const string& lhs, const string& rhs);
    

    返回一个新的字符串,其中包含了左右两个操作数字符串的内容按顺序连接在一起。

  2. 使用+=运算符进行字符串拼接和追加:

    string& operator+=(const string& str);
    string& operator+=(const char* str);
    string& operator+=(char c);
    

    将指定的字符串或字符追加到当前字符串的末尾,并更新当前字符串对象。

  3. 使用append()函数进行字符串拼接和追加:

    string& append(const string& str);
    string& append(const string& str, size_t subpos, size_t sublen);
    string& append(const char* s);
    string& append(const char* s, size_t n);
    string& append(size_t n, char c);
    

    在当前字符串的末尾追加指定的字符串、字符或重复字符,并更新当前字符串对象。

  4. 使用push_back()函数将字符追加到字符串末尾:

    void push_back(char c);
    

    将指定的字符追加到当前字符串的末尾。

这些拼接操作可以根据需要选择合适的方式来进行字符串的连接和追加操作。使用+运算符进行拼接可以方便地创建一个新的字符串,而使用+=运算符和append()函数则更适合在原字符串上进行追加操作。熟悉这些拼接操作的使用方法有助于进行灵活的字符串处理和拼接。

2.2.5、string查找和替换

在C++的string容器中,有以下用于查找和替换子串的成员函数:

  1. find()函数用于查找子串在字符串中的位置:

    size_type find(const basic_string& str, size_type pos = 0) const noexcept;
    size_type find(const char* s, size_type pos = 0) const noexcept;
    size_type find(const char* s, size_type pos, size_type count) const noexcept;
    size_type find(CharT ch, size_type pos = 0) const noexcept;
    
    • str版本:从指定位置pos开始在当前字符串对象中查找子串str
    • s版本:从指定位置pos开始在当前字符串对象中查找C风格字符串s
    • ch版本:从指定位置pos开始在当前字符串对象中查找字符ch

    这些函数返回子串第一次出现的位置(索引值),如果找不到子串,则返回basic_string::npos

  2. rfind()函数用于反向查找子串在字符串中的位置:

    size_type rfind(const basic_string& str, size_type pos = npos) const noexcept;
    size_type rfind(const char* s, size_type pos = npos) const noexcept;
    size_type rfind(const char* s, size_type pos, size_type count) const noexcept;
    size_type rfind(CharT ch, size_type pos = npos) const noexcept;
    

    rfind()函数从右向左在字符串中查找子串,并返回找到的最后一个子串的位置(索引值)。如果找不到子串,则返回basic_string::npos

  3. replace()函数用于替换字符串中的子串:

    basic_string& replace(size_type pos, size_type count, const basic_string& str);
    basic_string& replace(const_iterator first, const_iterator last, const basic_string& str);
    basic_string& replace(size_type pos, size_type count, const CharT* s, size_type count2);
    basic_string& replace(const_iterator first, const_iterator last, const CharT* s, size_type count2);
    basic_string& replace(size_type pos, size_type count, const CharT* s);
    basic_string& replace(const_iterator first, const_iterator last, const CharT* s);
    basic_string& replace(size_type pos, size_type count, size_type count2, CharT ch);
    

    replace()函数允许替换指定位置或范围内的子串为给定的字符串str或字符ch。可以通过位置或迭代器范围来指定替换的区域。

注意:上述函数中的nposbasic_string类中的静态成员,表示无效的位置。

2.2.6、string比较操作

在C++的string容器中,常用的字符串比较操作有以下几种成员函数:

  1. compare()函数用于比较两个字符串的大小关系:

    int compare(const basic_string& str) const noexcept;
    int compare(size_type pos, size_type count, const basic_string& str) const;
    int compare(size_type pos, size_type count, const basic_string& str, size_type subpos, size_type subcount) const;
    int compare(const CharT* s) const;
    int compare(size_type pos, size_type count, const CharT* s) const;
    int compare(size_type pos, size_type count, const CharT* s, size_type n) const;
    

    这些函数返回一个整数值,表示比较的结果:

    • 如果返回值为0,表示两个字符串相等。
    • 如果返回值小于0,表示当前字符串小于参数字符串。
    • 如果返回值大于0,表示当前字符串大于参数字符串。

    可以通过指定位置和长度范围来比较字符串的一部分。

  2. operator==operator!=operator<operator<=operator>operator>=等运算符重载也可以用于字符串之间的比较操作。例如:

    bool operator==(const basic_string& lhs, const basic_string& rhs) noexcept;
    bool operator!=(const basic_string& lhs, const basic_string& rhs) noexcept;
    bool operator<(const basic_string& lhs, const basic_string& rhs) noexcept;
    bool operator<=(const basic_string& lhs, const basic_string& rhs) noexcept;
    bool operator>(const basic_string& lhs, const basic_string& rhs) noexcept;
    bool operator>=(const basic_string& lhs, const basic_string& rhs) noexcept;
    

    这些运算符返回布尔值,表示比较的结果:true表示成立,false表示不成立。

这些字符串比较操作可以用于判断字符串的相等性、大小关系以及在排序中的比较。根据具体的需求,选择适当的比较方式来进行字符串操作。

2.2.7、string提取字串操作

在C++的string容器中,常用的提取字符串子串的操作有以下几种:

  1. substr()函数用于提取子字符串:

    basic_string substr(size_type pos = 0, size_type count = npos) const;
    

    这个函数接受两个参数,第一个参数pos表示开始提取的位置,第二个参数count表示要提取的字符数。默认情况下,pos为0,即从字符串的开头开始提取,countnpos,即提取从pos到字符串结尾的所有字符。

    例如:

    std::string str = "Hello, World!";
    std::string subStr1 = str.substr(7);           // 提取从位置7到结尾的所有字符,结果为"World!"
    std::string subStr2 = str.substr(7, 5);        // 提取从位置7开始的5个字符,结果为"World"
    
  2. 使用迭代器可以提取子串,通过指定起始位置和终止位置来获取子串的范围。

    例如:

    std::string str = "Hello, World!";
    auto start = str.begin() + 7;                   // 指向位置7的迭代器
    auto end = str.end();                           // 结束位置的迭代器
    std::string subStr(start, end);                 // 提取从位置7到结尾的所有字符,结果为"World!"
    

这些操作可以根据需要从字符串中提取出子串,灵活选择适当的方法和参数。记住,字符串的提取操作是基于位置和范围进行的。

2.2.8、string插入和删除操作

在C++的string容器中,常用的插入和删除操作有以下几种:

  1. 插入操作:

    • 在指定位置插入单个字符:

      iterator insert(iterator pos, char ch);
      
    • 在指定位置插入字符串:

      iterator insert(iterator pos, const char* s);
      iterator insert(iterator pos, const std::string& str);
      iterator insert(iterator pos, size_type count, char ch);
      
    • 在指定位置插入字符串的子串:

      iterator insert(iterator pos, const std::string& str, size_type subpos, size_type sublen);
      
  2. 删除操作:

    • 删除指定位置的单个字符:

      iterator erase(iterator pos);
      
    • 删除指定范围内的字符:

      iterator erase(iterator first, iterator last);
      
    • 清空整个字符串:

      void clear();
      

    例如:

    std::string str = "Hello, World!";
    
    // 插入操作示例
    str.insert(7, "beautiful");               // 在位置7插入字符串"beautiful"
    str.insert(7, 3, '*');                    // 在位置7插入3个'*'
    str.insert(7, "beauty", 3);               // 在位置7插入字符串"bea"的子串
    
    // 删除操作示例
    str.erase(7, 9);                          // 删除从位置7开始的9个字符
    
    // 清空整个字符串
    str.clear();                              // 清空字符串
    

这些操作可以用于在字符串中插入或删除字符,根据需求灵活选择适当的方法和参数。记住,插入和删除操作会改变原始字符串的内容和长度。

2.2.9、string和c语言风格字符串转换

在C++中,可以使用以下方法进行string和C语言风格字符串之间的转换:

  1. 将C语言风格字符串转换为string对象:

    const char* cStr = "Hello, World!";
    std::string str = cStr;  // 使用赋值运算符将C语言风格字符串赋给string对象
    
  2. 将string对象转换为C语言风格字符串:

    std::string str = "Hello, World!";
    const char* cStr = str.c_str();  // 使用c_str()函数获取C语言风格字符串指针
    

请注意下面两点:

  • 当将C语言风格字符串转换为string对象时,可以直接将其赋值给string对象。
  • 当将string对象转换为C语言风格字符串时,需要使用c_str()函数获取C语言风格字符串指针。

这些方法可以在string对象和C语言风格字符串之间进行简单的转换。使用适当的方法,根据需要进行转换。

2.3、使用示例

下面是一些使用示例,演示了C++中string容器的常用操作:

#include 
#include 

int main() {
  // 创建一个空的字符串
  std::string str;

  // 使用字符串字面值初始化字符串
  std::string hello = "Hello";

  // 使用+=运算符和append()函数进行字符串拼接和追加
  hello += ", ";
  hello.append("world!");

  // 使用+运算符进行字符串拼接
  std::string greeting = hello + " How are you?";

  // 访问和修改字符串中的字符
  char firstChar = greeting[0];
  greeting[0] = 'W';

  // 获取字符串的长度
  int length = greeting.length(); // 或者可以使用greeting.size()

  // 检查字符串是否为空
  bool isEmpty = greeting.empty();

  // 获取字符串的子串
  std::string substr1 = greeting.substr(6);       // 从索引位置6开始截取到末尾
  std::string substr2 = greeting.substr(6, 5);    // 从索引位置6开始截取长度为5的子串

  // 查找子串在字符串中的位置
  size_t pos = greeting.find("are");

  // 替换指定子串
  greeting.replace(pos, 3, "is");

  // 字符串迭代器遍历
  for (auto it = greeting.begin(); it != greeting.end(); ++it) {
      std::cout << *it;
  }
  std::cout << std::endl;

  // 获取C风格字符串指针
  const char* cStr = greeting.c_str();

  // 输出结果
  std::cout << greeting << std::endl;   // 输出:How world! How is you?

  return 0;
}

这些示例展示了string容器类的一些常用操作,包括字符串的初始化、拼接、访问和修改字符、获取长度、判断是否为空、截取子串、查找和替换子串等。通过灵活使用这些操作,可以对字符串进行各种处理和操作。

总结

C++的std::string容器是用来存储和操作字符串的常用工具。它提供了丰富的功能和方法来处理字符串数据。以下是对C++ std::string容器的总结:

  1. 创建和初始化:

    • 使用默认构造函数创建空字符串:

      std::string str;
      
    • 使用字符数组或C字符串初始化字符串:

      const char* cStr = "Hello, World!";
      std::string str(cStr);
      
    • 使用另一个字符串初始化:

      std::string str1 = "Hello";
      std::string str2(str1);
      
  2. 访问元素:

    • 使用下标运算符获取指定位置的字符:

      char ch = str[index];
      
    • 使用at()函数进行范围检查的访问:

      char ch = str.at(index);
      
  3. 修改和操作:

    • 追加字符串:

      str.append(" World");
      
    • 插入/删除字符或子串:

      str.insert(pos, "new ");
      str.erase(pos, length);
      
    • 替换子串:

      str.replace(pos, length, "new substring");
      
    • 字符串连接:

      std::string result = str1 + str2;
      str1 += str2;
      
  4. 查询信息:

    • 获取字符串长度:

      size_t length = str.length();
      
    • 判断字符串是否为空:

      bool isEmpty = str.empty();
      
    • 搜索子串位置:

      size_t pos = str.find("substring");
      
  5. 字符串比较:

    • 使用==!=<><=>=运算符比较字符串的大小关系:

      bool isEqual = (str1 == str2);
      
    • 使用compare()函数进行详细比较:

      int result = str1.compare(str2);
      
  6. 转换:

    • 将string转换为C语言风格的字符串:

      const char* cStr = str.c_str();
      
    • 将字符串转换为其他类型(如整数、浮点数):

      int num = std::stoi(str);
      
  7. 清空和截取:

    • 清空字符串内容:

      str.clear();
      
    • 截取子串:

      std::string sub = str.substr(pos, length);
      

【038】解码C++ STL:探索string容器的无限可能与鲜为人知的技巧_第2张图片

你可能感兴趣的:(C++从零开始到精通,c++,开发语言,c语言,数据结构,stl,容器,后端)