C++ 笔记(19)— 标准模板库(STL容器、STL迭代器、STL算法、STL容器特点、STL字符串类)

C++ 标准库可以分为两部分:

  • 标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。
  • 面向对象类库: 这个库是类及其相关函数的集合。

C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。

标准函数库

标准函数库分为以下几类:

  • 输入/输出 I/O
  • 字符串和字符处理
  • 数学
  • 时间、日期和本地化
  • 动态分配
  • 其他
  • 宽字符函数

面向对象类库
标准的 C++ 面向对象类库定义了大量支持一些常见操作的类,比如输入/输出 I/O、字符串处理、数值处理。面向对象类库包含以下内容:

  • 标准的 C++ I/O 类
  • String 类
  • 数值类
  • STL 容器类
  • STL 算法
  • STL 函数对象
  • STL 迭代器
  • STL 分配器
  • 本地化库
  • 异常处理类
  • 杂项支持库

简单地说,标准模板库( STL )是一组模板类和函数,向程序员提供了:

  • 用于存储信息的容器;
  • 用于访问容器存储的信息的迭代器;
  • 用于操作容器内容的算法。

1. STL 容器

容器是用于存储数据的 STL 类, STL 提供了两种类型的容器类:

  • 顺序容器;
  • 关联容器。

另外, STL 还提供了被称为容器适配器(Container Adapter)的类,它们是顺序容器和关联容器的变种,包含的功能有限,用于满足特殊的需求。

1.1 顺序容器

顾名思义,顺序容器按顺序存储数据,如数组和列表。顺序容器具有插入速度快但查找操作相对较慢的特征。

STL 顺序容器如下所示。

  • std::vector :操作与动态数组一样,在最后插入数据;可将 vector 视为书架,您可在一端添加和拿走图书。

  • std::deque :与 std::vector 类似,但允许在开头插入或删除元素。

  • std::list :操作与双向链表一样。可将它视为链条,对象被连接在一起,您可在任何位置添加或删除对象。

  • std::forward_list :类似于 std::list ,但是单向链表,只能沿一个方向遍历。

STL vector 类与数组类似,允许随机访问元素,即可使用下标运算符 [] 指定元素在 vector 中的位置(索引),从而直接访问或操作元素。另外, STL vector 是动态数组,因此能够根据应用程序在运行阶段的需求自动调整长度。

为保留数组能够根据位置随机访问元素的特征,大多数 STL vector 实现都将所有元素存储在连续的存储单元中,因此需要调整长度的 vector 通常会降低应用程序的性能,这取决于它包含的对象类型。

可将 STL list 类视为普通链表的 STL 实现。虽然 list 中的元素不能像 STL vector 中的元素那样随机访问,但 list 可使用不连续的内存块组织元素,因此它不像 std::vector 那样需要给内部数组重新分配内存,进而导致性能问题。

1.2 关联容器

关联容器按指定的顺序存储数据,就像词典一样。这将降低插入数据的速度,但在查询方面有很大的优势。

STL 提供的关联容器如下所示。

  • std::set :存储各不相同的值,在插入时进行排序;容器的复杂度为对数。
  • std::unordered_set :存储各不相同的值,在插入时进行排序;容器的复杂度为常数。这种容器是 C++11 新增的。
  • std::map :存储键-值对,并根据唯一的键排序;容器的复杂度为对数。
  • std::unordered_map :存储键-值对,并根据唯一的键排序;容器的复杂度为对数。这种容器是 C++11 新增的。
  • std::multiset :与 set 类似,但允许存储多个值相同的项,即值不需要是唯一的。
  • std::unordered_multiset :与 unordered_set 类似,但允许存储多个值相同的项,即值不需要是唯一的。这种容器是 C++11 新增的。
  • std::multimap :与 map 类似,但不要求键是唯一的。
  • std::unordered_multimap :与 unordered_map 类似,但不要求键是唯一的。这种容器是 C++11 新增的。

1.3 容器适配器

容器适配器(Container Adapter)是顺序容器和关联容器的变种,其功能有限,用于满足特定的需求。主要的适配器类如下所示。

  • std::stack :以 LIFO (后进先出)的方式存储元素,让您能够在栈顶插入(压入)和删除(弹出)元素。
  • std::queue :以 FIFO (先进先出)的方式存储元素,让您能够删除最先插入的元素。
  • std::priority_queue :以特定顺序存储元素,因为优先级最高的元素总是位于队列开头。

2. STL 迭代器

最简单的迭代器是指针。给定一个指向数组中的第一个元素的指针,可递增该指针使其指向下一个元素,还可直接对当前位置的元素进行操作。

STL 中的迭代器是模板类,从某种程度上说,它们是泛型指针。这些模板类让程序员能够对 STL 容器进行操作。注意,操作也可以是以模板函数的方式提供的 STL 算法,迭代器是一座桥梁,让这些模板函数能够以一致而无缝的方式处理容器,而容器是模板类。

STL 提供的迭代器分两大类。

  • 输入迭代器:通过对输入迭代器解除引用,它将引用对象,而对象可能位于集合中。最严格的输入迭代器确保只能以只读的方式访问对象。

  • 输出迭代器:输出迭代器让程序员对集合执行写入操作。最严格的输出迭代器确保只能执行写入操作。

上述两种基本迭代器可进一步分为三类。

  • 前向迭代器:这是输入迭代器和输出迭代器的一种细化,它允许输入与输出。前向迭代器可以是 const 的,只能读取它指向的对象;也可以改变对象,即可读写对象。前向迭代器通常用于单向链表。
  • 双向迭代器:这是前向迭代器的一种细化,可对其执行递减操作,从而向后移动。双向迭代器通常用于双向链表。
  • 随机访问迭代器:这是对双向迭代器的一种细化,可将其加减一个偏移量,还可将两个迭代器相减以得到集合中两个元素的相对距离。随机访问迭代器通常用于数组。

3. STL 算法

查找、排序和反转等都是标准的编程需求,不应让程序员重复实现这样的功能。因此 STLSTL 算法的方式提供这些函数,通过结合使用这些函数和迭代器,程序员可对容器执行一些最常见的操作。

最常用的 STL 算法如下所示。

  • std::find :在集合中查找值。
  • std::find_if :根据用户指定的谓词在集合中查找值。
  • std::reverse :反转集合中元素的排列顺序。
  • std::remove_if :根据用户定义的谓词将元素从集合中删除。
  • std::transform :使用用户定义的变换函数对容器中的元素进行变换。

这些算法都是 std 命名空间中的模板函数,要使用它们,必须包含标准头文件 <algorithm>

4. 迭代器在容器和算法之间交互

下面程序使用了 STL 顺序容器 std::vector (它类似于动态数组)来存储一些整数,再使用 std::find 算法在集合中查找一个整数。请注意迭代器是如何在这两者之间搭建桥梁的。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main ()
{
    // A dynamic array of integers
    vector <int> intArray;

    // Insert sample integers into the array
    intArray.push_back (50);
    intArray.push_back (2991);
    intArray.push_back (23);
    intArray.push_back (9999);

    cout << "The contents of the vector are: " << endl;

    // Walk the vector and read values using an iterator
    vector <int>::iterator arrIterator = intArray.begin ();

    while (arrIterator != intArray.end ())
    {
        // Write the value to the screen
        cout << *arrIterator << endl;

        // Increment the iterator to access the next element
        ++ arrIterator;
    }

    // Find an element (say 2991) using the 'find' algorithm
    vector <int>::iterator elFound = find (intArray.begin () , intArray.end (), 2991);

    
    // Check if value was found
    if (elFound != intArray.end ())
    {
        // Determine position of element using std::distance
        int elPos = distance (intArray.begin (), elFound);
        cout << "Value "<< *elFound;
        cout << " found in the vector at position: " << elPos << endl;
    }

    return 0;
}

输出结果:

The contents of the vector are: 
50
2991
23
9999
Value 2991 found in the vector at position: 1

上述代码演示了如何使用迭代器遍历向量。迭代器是一个接口,将算法( find )连接到其要操作的数据所属的容器(如 vector )。

第 20 行声明了迭代器对象 arrIterator ,并将其初始化为指向容器开头,即 vector 的成员函数 begin() 返回的值。

第 22~29 行演示了如何在循环中使用该迭代器遍历并显示 vector 包含的元素,这与显示静态数组的内容极其相似。迭代器的用法在所有 STL 容器中都相同。

所有容器都提供了 begin() 函数和 end() 函数,其中前者指向第一个元素,后者指向容器中最后一个元素的后面。这就是第 22 行的 while 循环在 end() 前面而不是 end() 处结束的原因。

第 32 行演示了如何使用 findvector 中查找值。 find 操作的结果也是一个迭代器,通过将该迭代器与容器末尾进行比较,可判断 find 是否成功,如第 36 行所示。如果找到了元素,便可对该迭代器解除引用(就像对指针解除引用一样)以显示该元素。

算法 distance 计算找到的元素的所处位置的偏移量。上述代码所有的 vector 都替换为 deque ,代码仍能通过编译并完美地运行。这表明迭代器让您能够轻松地使用算法和容器。

5. STL 容器类特点

图1
图2

6. STL 字符串类

STL 提供了一个专门为操纵字符串而设计的模板类: std::basic_string<T> ,该模板类的两个常用具体化如下所示。

  • std::string :基于 charstd::basic_string 具体化,用于操纵简单字符串。
  • std::wstring :基于 wchar_tstd::basic_string 具体化,用于操纵宽字符串,通常用于存储支持各种语言中符号的 Unicode 字符。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wohu007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值