STL标准模板库
STL的诞生
-
软件界一直希望建立一种可重复利用的东西。
-
c++的面向对象和泛型编程思想,目的是复用性的提升。
-
大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作。
-
建立数据结构和算法的一套标准,所以诞生了STL。
STL基本概念
-
STL(标准模板库),是一种泛型编程。面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法,它们之间的共同点是抽象和创建可重用代码,但理念决然不同。
-
泛型编程旨在编写独立于数据类型的代码。在c++中,完成通用程序的工具是模板。模板使得能够按泛型定义函数或类,而STL通过通用算法更进了一步。模板让这一切成为可能。
-
STL三大组件:容器,算法,迭代器;(算法操作数据,容器存储数据,迭代器是算法操作器的桥梁,迭代器和容器一一对应)。
-
STL几乎所有的代码都采用了模板或模板函数。
STL六大组件
-
容器:各种数据结构,是一个概念化的抽象基类,容器不真正使用继承机制,容器概念指定了所有STL容器类都必须满足的一系列要求。如:vector list deque set map等,用来存放数据。
-
算法:各种常用的算法,如:sort,find,copy,for_each等。
-
迭代器:容器和算法直接的桥梁。
-
仿函数:为算法提供更多的策略。
-
适配器:为算法提供更多的参数接口。
-
空间适配器:管理容器和算法的空间。
STL主要头文件
<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>、<utility>、<unordered_set>、<unordered_m>
//1.算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。
<algorithm>是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。
<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
<functional>中则定义了一些模板类,用以声明函数对象。
//2.容器部分主要由头文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>组成。对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。
向量(vector) 连续存储的元素<vector>
列表(list) 由节点组成的双向链表,每个结点包含着一个元素<list>
双队列(deque) 连续存储的指向不同元素的指针所组成的数组<deque>
集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序 <set>
多重集合(multiset) 允许存在两个次序相等的元素的集合 <set>
栈(stack) 后进先出的值的排列 <stack>
队列(queue) 先进先出的执的排列 <queue>
优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列 <queue>
映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 <map>
多重映射(multimap) 允许键对有相等的次序的映射 <map>
无序集合(unordered_set) 不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符 <unordered_set>
无序多重集合(unordered_multiset) 不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符 <unordered_set>
无序映射(unordered_map) 不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符 <unordered_map>
无序多重映射(unordered_multimap) 不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符 <unordered_map>
//3.迭代器部分主要由头文件<utility>,<iterator>和<memory>组成。
<utility>是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,
<iterator>中提供了迭代器使用的许多方法,而对于<memory>的描述则十分的困难,它以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生的临时对象提供机制,<memory>中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。
泛型算法
标准库定义了一些泛型算法,作为实现容器的某些操作(查找、拷贝、排序等)的公共接口。泛型意味着这些算法可以用于不同类型的元素和多种容器类型。泛型算法本身不执行容器的操作,它们运行在迭代器之上,执行迭代器的操作。泛型算法一般定义在头文件
algorithm
中。此外在numeric
也定义了一组数组泛型算法。下面具体记录几个常用的泛型算法的使用方法。
find 查找
使用
find
进行查找,传递给find的前两个元素指示查找范围,第三个元素指示要找到元素值。若找到与给定元素值相等的元素,则返回指向第一个给定值的元素的迭代器,若无匹配元素,则返回第二个参数。因此我们可以通过判断该算法是返回值是否等于传入的第二个参数来判断指定范围中是否存在我们要找的元素。
vector<int> nums = { 5,4,7,8,9,6,3,2,1 };
auto fun = [&nums](vector<int>::iterator it) { if (it == nums.end())
cout << "Can't find" << endl;
else
cout << *it << endl; };
int val1 = 9, val2 = 10;
auto it1 = find(nums.begin(), nums.end(), val1);//找与val1相等的元素
auto it2 = find(nums.begin(), nums.end(), val2);//找与val2相等的元素
fun(it1);//输出9
fun(it2);//输出Can't find
输出:
copy 复制
使用
copy
将一个容器中的指定范围的元素拷贝到目标序列的指定位置。传入的前两个参数指示要拷贝的元素的范围,第三个参数指示拷贝到的目标序列的起始位置。copy
返回指向拷贝到目标序列的最后一个元素的后一位置(即尾后位置)的迭代器。注意目标序列应有足够的空间容纳指定范围的元素。在如下示例中,
nums1
中指定范围的元素被拷贝到nums2
的指定位置。通过输出可以看到拷贝成功,同时返回的迭代器确实指向拷贝完成后的尾后位置。
vector<int> nums1 = { 5,4,7,8,9,6,3,2,1 };
vector<int> nums2(nums1.size());
cout << "nums1:" << " ";
for (auto num : nums1) {
cout << num << ' ';
}
cout << endl << "nums2_before:" << " ";
for (auto num : nums2) {
cout << num << ' ';
}
auto res = copy(nums1.begin() + 1, nums1.begin() + 6, nums2.begin() + 2);
cout << endl << "nums2_now:" << " ";
for (auto num : nums2) {
cout << num << ' ';
}
cout << endl << *res << ' ' << *(res - 1) << ' ' << *(res + 1);
输出:
sort 排序
使用
sort
进行排序,前两个参数指定排序范围,第三个参数指示排序规则,若不给出第三个参数,则默认采用升序排序。
vector<int> nums = { 5,4,7,8,9,6,3,2,1 };
sort(nums.begin(), nums.end());
for (auto num : nums) {
cout << num << ' ';
}
输出:
当然,可以向
sort
传入第三个参数,自定义排序规则,比较方便的做法是传入一个lambda表达式。
vector<int> nums = { 5,4,7,8,9,6,3,2,1 };
sort(nums.begin(), nums.end(), [](int& a, int& b) { return a > b; });
for (auto num : nums) {
cout << num << ' ';
}
输出: