在C++的STL(Standard Template Library,标准模板库)中,所谓的"分配器"(Allocators)是一个用于抽象内存模型的组件,用于定义内存的分配、释放以及对象的构造和析构。STL分配器提供了一个统一的接口,使容器能够与之无缝配合进行内存管理。
STL六大组件包括:
- 容器(Containers):用于存储数据的类模板集合,比如vector、list、map等。
- 算法(Algorithms):对数据执行各种操作的函数模板,比如排序、查找和变换等。
- 迭代器(Iterators):类似于指针,用于遍历容器中的元素。
- 分配器(Allocators):定义内存模型,包括分配和释放内存,以及构造和析构对象。
- 适配器(Adaptors):用于改变容器或函数或迭代器的接口。
- 仿函数(Functors, 也称函数对象):可以像函数一样使用的对象,通通过重载 operator() 进行调用。
分配器通常被设计为模板类,定义了几个基本的成员类型和操作,包括:
- allocate:分配未初始化存储空间。
- deallocate:释放之前分配的存储空间。
- construct:在未初始化存储空间上构造对象。
- destroy:析构在存储空间上已构造的对象。
默认的STL分配器是std::allocator,但是用户可以定义自己的分配器以适应特定的内存分配需求,只要这个分配器符合STL所要求的分配器接口规范。
STL的分配器用于封装STL容器在内存管理上的底层细节。在C++中,其内存配置和释放如下:
new运算分两个阶段:(1)调用::operator new配置内存;(2)调用对象构造函数构造对象内容
delete运算分两个阶段:(1)调用对象希构函数;(2)调用员工::operator delete释放内存 为了精密分工,STL allocator将两个阶段操作区分开来:内存配置有alloc::allocate()负责,内存释放由alloc::deallocate()负责;对象构造由::construct()负责,对象析构由::destroy()负责。 同时为了提升内存管理的效率,减少申请小内存造成的内存碎片问题,SGI STL采用了两级配置器,当分配的空间大小超过128B时,会使用第一级空间配置器;当分配的空间大小小于128B时,将使用第二级空间配置器。第一级空间配置器直接使用malloc()、realloc()、free()函数进行内存空间的分配和释放,而第二级空间配置器采用了内存池技术,通过空闲链表来管理内存。
几种常见容器对比:
set, map, multiset, multimap, unordered_set, 和 unordered_map 都是C++标准库(STL)提供的关联容器。每个容器有其独特的特点和用途:
- set:
- set 是一个集合,其中包含唯一的元素,不允许重复。
- 内部通常实现为有序的平衡二叉树(如红黑树),元素按照指定的顺序排序。
- 查找、插入和删除操作的时间复杂度为 O(log n)。
- map:
- map 是一组键值对,键是唯一的,不允许重复,而值则可以重复。
- 同样基于有序的平衡二叉树实现,使用键对元素进行排序和搜索。
- 查找、插入和删除操作的时间复杂度均为 O(log n)。
- multiset:
- multiset 类似于 set,但它允许元素重复。
- 也是基于有序的平衡二叉树实现,元素按照特定规则排序。
- 查找特定元素的时间复杂度为 O(log n),查找所有相同元素的时间复杂度为 O(log n + k),k 是重复元素的数量。
- multimap:
- multimap 类似于 map,但每个键可以对应多个值,即允许键的重复。
- 内部结构同样是基于有序的平衡二叉树。
- 查找所有键相等的键值对的时间复杂度为 O(log n + k),k 是具有相同键的键值对的数量。
- unordered_set:
- unordered_set 和 set 功能类似,但它不保持元素的有序性,内部使用哈希表实现。
- 由于是哈希实现,平均情况下查找、插入和删除的时间复杂度为 O(1),但在最坏情况下可能退化到 O(n)。
- unordered_map:
- unordered_map 是由键控制的唯一值的集合,功能与 map 类似,但不维护任何元素顺序,内部使用哈希表实现。
- 平均情况下查找、插入和删除操作的时间复杂度为 O(1),最坏情况下为 O(n)。
总结和选用建议:
- 如果需要有序的元素集合,请使用 set 或 map。
- 如果需要存储重复元素,请使用 multiset 或 multimap。
- 如果元素的顺序不重要,且希望实现快速访问,使用 unordered_set 或 unordered_map 可以提供更好的性能。
- 哈希表基于的存储机制意味着 unordered_ 容器通常有较快的查找和插入时间,但是它们不保证遍历的顺序,且在处理较大元素或负载因子较高时的性能可能会降低。