自学内容网 自学内容网

C++初阶(十)--初识STL

目录

​编辑

一、STL 是什么?

二、STL的六大组件

容器(Containers)

迭代器(Iterators)

算法(Algorithms)

函数对象(Function Objects)

适配器(Adapters)

空间配置器(Allocator)

三、为什么要用 STL?


一、STL 是什么?

STL 是 C++ 标准库的一部分,它提供了一系列通用的模板类和函数模板,这些模板都是经过精心设计和优化的,可以用来处理各种常见的编程任务,比如数据存储(像数组、链表、树这些结构相关的事儿)、数据处理(排序、查找等操作)以及算法实现等。简单说呢,就是别人已经帮咱们把很多常用的、好用的代码模板都准备好了,咱们只要知道怎么用就行了。

二、STL的六大组件

STL(Standard Template Library,标准模板库)主要由以下六大组件构成,每个组件在 STL 体系中都起着至关重要的作用,它们相互协作,为 C++ 程序员提供了强大且便捷的编程工具。

容器(Containers)

容器是用于存储数据元素的对象,STL 提供了多种不同类型的容器,以满足各种数据存储和操作需求。

  • 顺序容器(Sequential Containers)

    • vector:类似于动态数组,可以在运行时动态调整大小。元素在内存中是连续存储的,这使得它能够快速地随机访问元素,但在中间插入或删除元素时,可能需要移动大量其他元素。例如,vector<int> vec; vec.push_back(1); vec.push_back(2);创建了一个存放整数的vector容器,并依次添加了两个元素。
    • list:是一个双向链表结构,每个节点包含一个数据元素以及指向前一个和后一个节点的指针。在链表中间插入和删除元素相对容易,只需调整相关节点的指针即可,不过随机访问元素的速度相对较慢,因为需要从链表头或链表尾逐个遍历节点才能访问到目标元素。比如,list<int> lst; lst.push_back(1); lst.push_back(2);创建了一个存放整数的list容器并添加了两个元素。
    • deque:即双端队列,它结合了数组和链表的一些特点。在两端(头部和尾部)可以像链表一样方便地插入和删除元素,同时又能像数组一样支持快速的随机访问(但随机访问速度比vector稍慢一些)。例如,deque<int> dq; dq.push_back(1); dq.push_back(2); dq.push_front(3);创建了一个存放整数的deque容器,不仅能从尾部添加元素,还能从头部添加元素。
  • 关联容器(Associative Containers)

    • set:是一种存储唯一元素的容器,它会自动按照元素的某种特定顺序(通常是根据元素的比较函数确定的顺序,如对于整数是按照大小顺序,对于字符串是按照字典序)对元素进行排序。例如,set<int> s; s.insert(3); s.insert(1); s.insert(2);创建了一个存放整数的set容器,并插入了几个元素,插入后元素会自动按照从小到大的顺序排列。当试图插入一个已经存在于set中的元素时,插入操作将不会成功,因为set要保证元素的唯一性。
    • map:它是一种键值对容器,每个元素由一个键(key)和一个值(value)组成。通过键可以快速地找到对应的 值,并且键在总体内是唯一的。例如,map<int, string> m; m.insert(make_pair(1, "one")); m.insert(make_pair(2, "one"));创建了一个存放整数键和字符串值的map容器,并插入了两对键值对。类似于setmap也会根据键的比较函数自动对键值对进行排序,以便于快速查找。

迭代器(Iterators)

迭代器是一种类似于指针的对象,它提供了一种统一的方式来访问容器中的元素,使得程序员可以在不了解容器内部实现细节的情况下,对不同类型的容器进行遍历、修改等操作。

  • 不同类型的容器有不同类型的迭代器,比如:
    • 正向迭代器:用于按照容器中元素的存储顺序从前往后遍历元素。例如,对于vector<int>容器,vector<int>::iterator it = vec.begin();,这里vec.begin()返回的就是指向容器第一个元素的正向迭代器,通过it++可以逐个访问后面的元素。
    • 反向迭代器:用于按照与容器中元素存储顺序相反的方向从后往前遍历元素。例如,对于list<int>容器,list<int>::reverse_iterator rit = lst.rbegin();,这里lst.rbegin()返回的就是指向容器最后一个元素的反向迭代器,通过rit--可以逐个访问前面的元素。
    • 常量迭代器:用于在不修改容器元素的情况下遍历元素,有正向常量迭代器和反向常量迭代器之分。例如,对于set<int>容器,set<int>::const_iterator cit = s.begin();,在这种情况下,虽然可以访问元素,但不能对其进行修改。

算法(Algorithms)

STL 提供了大量的算法来对容器中的元素进行各种操作,这些算法通过迭代器来指定操作的范围,从而实现了对不同类型容器的通用性。

  • 排序算法(Sorting Algorithms)

    • std::sort:这是最常用的排序算法之一,它可以对一个给定范围内的元素进行快速排序。例如,对于vector<int>容器vecstd::sort(vec.begin(), vec.end());会将容器中的整数按照从小到大的顺序排列。它的时间复杂度通常为 O (n log n),其中 n 是要排序的元素个数。
    • std::stable_sort:与std::sort类似,但它在排序过程中会保证相等元素的相对顺序不变。例如,在对一个包含重复元素的容器排序时,如果使用std::stable_sort,那么原来在前面的重复元素在排序后仍然会在前面。
  • 查找算法(Searching Algorithms)

    • std::find:用于在一个给定范围内寻找指定的元素。例如,对于list<int>容器lststd::find(lst.begin(), lst.end(), 5);会在容器中寻找整数 5,如果找到则返回指向该元素的迭代器,否则返回一个特殊的标志(如list<int>::end()),表示没有找到。
    • std::binary_search:适用于已经排序的容器,它利用二分法来快速查找指定的元素。例如,对于已经排序好的vector<int>容器vecstd::binary_search(vec.begin(), vec.end(), 5);会判断容器中是否存在整数 5。如果存在,返回 true;否则,返回 false。
  • 其他算法(Other Algorithms)

    • std::copy:用于将一个范围内的元素复制到另一个位置。例如,对于vector<int>容器vec1vec2std::copy(vec1.begin(), vec1.end(), vec2.begin());会将vec1的所有元素复制到vec2的开头部分。
    • std::accumulate:用于计算一个范围内的元素的总和或其他可累积的属性。例如,对于vector<int>容器vecstd::accumulate(vec.begin(), vec.end(), 0);会计算容器中所有元素的总和,这里第二个参数 0 表示初始值。

函数对象(Function Objects)

函数对象,也称为仿函数(Functors),是一种可以像函数一样被调用的对象。它们在 STL 中有着重要的作用,常常被用来替代普通的函数作为算法的参数,以实现更灵活的操作。

  • 例如,定义一个比较函数对象来实现特定的比较规则:
    • class MyComparator { public: bool operator()(int a, int b) { return a > b; } };
    • 这里定义了一个名为MyComparator的函数对象,它实现了一个比较规则,即比较两个整数时,返回较大的数在前的结果。可以将这个函数对象作为参数传给std::sort算法,例如:vector<int> vec; vec.push_back(1); vec.push_back(2); std::sort(vec.begin(), vec.end(), MyComparator());,这样就会按照我们定义的比较规则(较大数在前)对容器中的元素进行排序。

适配器(Adapters)

适配器是一种将一种接口转换为另一种接口的组件,在 STL 中主要用于将不同类型的容器、迭代器或函数对象进行转换,以满足特定的应用需求。

  • 容器适配器(Container Adapters)

    • stack:它是基于其他容器(如vectorlist等)实现的一种后进先出(LIFO)的数据结构。例如,以vector为基础实现的stack可以这样创建:stack<int, vector<int>> stk;,这里第一个参数表示栈中元素的类型,第二个参数表示所基于的容器类型。通过pushpop等操作可以对栈进行操作。
    • queue:也是基于其他容器(如vectorlist等)实现的一种数据结构,不过它是先进先出(FIFO)的。例如,以list为基础实现的queue可以这样创建:queue<int, list<int>> q;,同样,通过pushpush_frontpop等操作可以对队列进行操作。
  • 迭代器适配器(Iterator Adapters)

    • reverse_iterator:它是一种将正向迭代器转换为反向迭代器的适配器。例如,对于vector<int>容器vecvector<int>::reverse_iterator rit = vec.rbegin();,这里vec.rbegin()就是利用reverse_iterator适配器将正向迭代器转换为反向迭代器的结果,通过rit--可以按照与容器中元素存储顺序相反的方向从后往前遍历元素。
  • 函数对象适配器(Function Object Adapters)

    • bind:它是一种将函数对象与参数进行绑定的适配器。例如,假设有一个函数对象MyFunction和一些参数,通过bind可以将这些参数绑定到MyFunction上,形成一个新的函数对象,以便于在特定的应用场景下使用。

空间配置器(Allocator)

空间配置器是负责为容器分配和回收内存空间的组件。它就像是一个幕后的 “内存管家”,当容器需要存储新的元素、扩展容量等情况时,就由空间配置器来处理相关的内存操作,确保容器能够顺利地获取到所需的内存空间,并且在元素被移除或容器被销毁时,合理地回收内存,避免内存泄漏等问题。

  • 一级空间配置器(__malloc_alloc_template):实际上是对mallocfree函数的一种包装。它在内部基本就是直接调用malloc函数来分配内存,调用free函数来回收内存。例如,当容器需要分配一定大小的内存空间时,一级空间配置器会像这样操作:void* ptr = __malloc_alloc_template<>::allocate(n);,这里n是需要分配的 ,__malloc_alloc_template<>::allocate函数内部就是调用malloc函数来完成分配的。
  • 二级空间配置器(__allocator_base):引入了内存池的概念。它会预先分配一块较大的内存池,然后从这个内存池中为容器分配所需的内存空间。比如,它可能会先分配一块 16KB 的内存池,当容器需要分配内存时,就从这个内存池中取出合适大小的 ,以便给容器。对于较小的内存块(通常定义为小于等于 128 字节),二级空间配置器会使用一种叫做 “自由链表” 的结构来管理。自由链表就是把同类型(即相同大小)的空闲内存块用链表的形式连接起来,当需要分配这种小内存块时,就从相应的自由链表中取出一个空闲块给容器;当容器释放这种小内存块时,又会把它放回相应的自由链表中,以便下次再利用。对于较大的内存块(大于 128 字节),二级空间配置器会像一级空间配置器一样,直接调用malloc函数来分配内存,并在回收时调用free函数。

这就是 STL 的六大组件啦,它们相互配合,为 C++ 程序员提供了高效、便捷的编程体验,使得处理各种数据结构和算法问题变得更加容易。

三、为什么要用 STL?

  • 提高开发效率:不用再从头去写那些处理数据结构和算法的代码啦,直接拿 STL 里的现成模板来用就行,能节省大量的时间和精力,让我们可以把更多的心思放在解决具体的业务问题上呢。
  • 代码质量保证:STL 里的模板都是经过专业人员精心设计和优化的,所以在性能、稳定性等方面都有很好的表现。使用 STL 可以让我们的代码质量更高,减少因为自己编写代码不当而导致的各种问题呢。
  • 代码的通用性和可移植性:因为 STL 是 C++ 标准库的一部分,所以在任何支持 C++ 标准的环境下都可以使用,这使得我们的代码具有很好的通用性和可移植性,不管是在 Windows、Linux 还是其他操作系统上,只要是 C++ 能跑的地方,STL 就能派上用场啦。


 本篇博客只是对STL初步认识,后面会对STL中的东西具体讲解,欢迎评论区留言


原文地址:https://blog.csdn.net/weixin_65132948/article/details/143508096

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!