1、STL
STL是标准模板库:是高效的C++程序库,其采用泛型编程思想对常见数据结构(顺序表、链表、栈和队列、堆、二叉树、哈希)和算法(查找、排序、集合、数值运算)等进行封装,体现着泛型编程程序设计思想以及设计模式,已被集成到C++标准程序库中。
STL中包含了容器、适配器、算法、迭代器、仿函数以及空间配置器。
STL设计理念:追求代码高复用性以及运行速度的高效率。
2、STL六大组件
容器
分为序列式容器和关联式容器。
容器适配器:
- stack:底层是对deque的重新封装
- queue:底层是对deque的重新封装
- priority_queue:底层是对vector的重新封装,大根堆
序列式容器:
- array:静态顺序表
- string:字符串
- vector:动态顺序表
- forward_list:带头节点的循环单链表
- list:带头节点的双向循环链表
- deque:动态二维数组
关联式容器:
- map:红黑树结构,key唯一键值对
- multimap:红黑树结构,key可重复键值对
- set:红黑树结构,value唯一
- multiset:红黑树结构,value可重复
- unordered_map:哈希结构,key唯一键值对
- unordered_multimap:哈希结构,key可重复键值对
- unordered_set:哈希结构,value唯一
- unordered_multiset:哈希结构,value可重复
算法
算法:问题的求解步骤,以有限的步骤解决数学或逻辑中的问题。
STL中的算法主要分为两大类:与数据结构相关算法(容器中的成员函数)和通用算法(与数据结构不相干)。
STL中通用算法主要包含:排序、查找、排列组合、数据移动、拷贝、删除、比较组合、运算等。STL常用算法
如find算法,find算法在查找时,与具体的数据结构无关,只要给出待查找数据集合的范围,find就可在该范围中查找,找到返回该元素在区间中的位置,否则返回end。
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value)
{
while(first != last)
{
if(*first == value)
break;
first++;
}
return first;
}
迭代器
迭代器是一种设计模式,让用户通过特定的接口访问容器的数据,不需要了解容器内部的底层数据结构。C++中迭代器本质是一个指针,让该指针按照具体的结构去操作容器中的数据。
每个容器的底层结构都不同,为了降低算法使用容器时的复杂度,底层结构应该对于算法透明,迭代器就充当了算法与容器之间的转接层,因此每个容器的迭代器应该由容器设计者负责提供,然后容器按照约定给出统一的接口即可。
实现原理:
容器底层结构不同,导致其实现原理不同,容器迭代器的设计,必须结合具体容器的底层数据结构。比如vector与list,因为vector底层结构为一段连续空间,迭代器前后移动时比较容易实现,因此vector的迭代器实际是对原生态指针的封装:typedef T* iterator;
。list底层结构为带头结点的双向循环链表,迭代器在移动时只能按照链表的结构前后依次移动,因此链表的迭代器需要对原生态的指针进行封装,因为当对迭代器++时,应该通过节点中的next指针域找到下一个节点:typedef list_iterator<T, T&, T*> iterator;
如果迭代器不能直接使用原生态指针操作底层数据时,必须要对指针进行封装,在封装时需要提供以下方法:
- 迭代器能够像指针一样方式进行使用:重载
pointer operator*()
/reference operator->()
- 能够让迭代器移动:向后移动重载
self& operator++()
/self operator++(int)
;向前移动重载self& operator--()
/self operator--(int)
- 支持比较(因为在遍历时需要知道是否移动到区间的末尾):
bool operator!=(const self& it) const
/bool operator==(const self& it) const
迭代器类的实现:
- 定义迭代器类(如list)
- 在容器类中统一迭代器名字
template<class T, class Alloc = alloc>
class list
{
typedef __list_iterator<T, T&, T*> iterator;
};
- 在容器类中添加获取迭代器范围的接口
template<class T, class Alloc = alloc>
class list
{
typedef __list_iterator<T, T&, T*> iterator;
iterator begin()
{
return (link_type)((*node).next);
}
iterator end()
{
return node;
}
};
适配器
适配器:将一个类的接口转换成用户希望的另一个类的接口,使原本接口不兼容的类可以一起工作。STL适配器有三种:
- 容器适配器:stack、queue
- 迭代器适配器:反向迭代器。反向迭代器++和–操作刚好和正向迭代器相反,因此反向迭代器只需将正向迭代器进行重新封装即可。
- 函数适配器
仿函数
仿函数:一种具有函数特征的对象,调用者可以像函数一样使用该对象,为了能够“行为类似函数”,该对象所在类必须自定义函数调用运算符operator(),重载该运算符后,就可在仿函数对象的后面加上一对小括号,以此调用仿函数所定义的operator()操作。就其行为而言,“仿函数”一次更切贴。仿函数一般配合算法,提高算法的灵活性。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class _mul_two
{
public:
void operator()(int& data)
{
data <<= 1;
}
};
class _mod_three
{
public:
bool operator()(int data)
{
return data % 3 == 0;
}
};
int main()
{
vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
for_each(vec.begin(), vec.end(), _mul_two());
for(auto e : vec)
{
cout<<e<<" ";
}
cout<<endl;
auto pos = remove_if(vec.begin(), vec.end(), _mod_three());
vec.erase(pos, vec.end());
for_each(vec.begin(), vec.end(), [](int data){cout<<data<<" ";});
cout<<endl;
return 0;
}
空间配置器
空间配置器:顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的,在默默地工作。