C++ 具名要求: Allocator
封装访问/寻址,分配/解分配,以及对象的构造/析构的策略。
可能需要分配或释放内存的每个标准库组件,从 std::string
、std::vector
和 std::array
以外的所有容器,到 std::shared_ptr
和 std::function
,都通过 Allocator分配器 进行这些操作:分配器是满足下列要求的类类型对象。
某些要求是可选的:模板 std::allocator_traits 为所有可选要求提供默认实现,且所有标准库容器和其他具分配器类,都是通过 std::allocator_traits
访问分配器,而并不直接访问。
要求
给定
- 无 cv 限定的对象类型
T
- 针对类型
T
的分配器 (Allocator) 类型A
-
A
类型的对象a
-
B
,为针对某个无 cv 限定的对象类型U
的对应分配器 (Allocator) 类型(重绑定A
所得) -
B
类型的对象b
-
allocator_traits<A>::pointer
类型的值ptr
,由调用 allocator_traits<A>::allocate() 所获得 -
allocator_traits<A>::const_pointer
类型的值cptr
,从ptr
转换而获得 -
allocator_traits<A>::void_pointer
类型的值vptr
,从ptr
转换而获得 -
allocator_traits<A>::const_void_pointer
类型的值cvptr
,从cptr
或vptr
转换而获得 -
xptr
,为针对某各无 cv 限定对象类型X
的可解引用指针 -
r
,为从表达式 *ptr 获得的T
类型的左值 -
allocator_traits<A>::size_type
类型的值n
表达式 | 要求 | 返回类型 |
---|---|---|
A::pointer (可选) | 满足可空指针 (NullablePointer) 、遗留随机访问迭代器 (LegacyRandomAccessIterator) 及遗留连续迭代器 (LegacyContiguousIterator) (见下文缀饰指针) | |
A::const_pointer (可选) | A::pointer 可转换为 A::const_pointer 。可空指针 (NullablePointer) 、遗留随机访问迭代器 (LegacyRandomAccessIterator) 及遗留连续迭代器 (LegacyContiguousIterator)
|
|
A::void_pointer (可选) | A::pointer 可转换为 A::void_pointer
|
|
A::const_void_pointer (可选) | A::pointer 、A::const_pointer 及 A::void_pointer 可转换到 A::const_void_pointer
|
|
A::value_type | 类型 T
| |
A::size_type (可选) | A::size_type 能表示 A 所能分配的最大对象的大小
|
无符号整数类型 |
A::difference_type (可选) | A::difference_type 能表示任意两个指向 A 所分配对象的指针之间的差
|
有符号整数类型 |
A::template rebind<U>::other (可选)[1] | 对于任何 U ,B::template rebind<T>::other 为 A
|
类型 B
|
*ptr | T&
| |
*cptr | *cptr 与 *ptr 标识同一对象 | const T&
|
ptr->m | 若 (*ptr).m 良构,则与 (*ptr).m 相同 | T::m 的类型
|
cptr->m | 若 (*cptr).m 良构,则与 (*cptr).m 相同 | T::m 的类型
|
static_cast<A::pointer>(vptr) | static_cast<A::pointer>(vptr) == ptr | A::pointer
|
static_cast<A::const_pointer>(cvptr) | static_cast<A::const_pointer>(cvptr) == cptr | A::const_pointer
|
std::pointer_traits<A::pointer>::pointer_to(r) | A::pointer
| |
a.allocate(n) | 分配适合 n 个 T 类型对象的存储,但不构造它们。可以抛异常。
|
A::pointer
|
a.allocate(n, cvptr) (可选) | 与 a.allocate(n) 相同,但可以以未指明的方式使用 cvptr (从 a.allocate() 获得的指针或 nullptr_t )以协助局部性
|
A::pointer
|
a.deallocate(ptr, n) | 解分配 ptr 所指向的存储,该指针必须是先前从对 allocate 的调用获得的指针,且未因中间对 deallocate 的调用而失效。n 必须匹配先前传递给 allocate 的值。不抛异常。
|
(不使用) |
a.max_size() (可选) | 能传递给 A::allocate() 的最大值
|
A::size_type
|
a1 == a2 | 仅若分配器 a1 分配的存储能通过 a2 解分配才返回 true。建立自反、对称和可传递关系。不抛异常。
|
bool |
a1 != a2 | 与 !(a1==a2) 相同 | bool |
A a1(a)
A a1 = a |
复制构造 a1 以满足 a1 == a。不抛异常。(注意:所有分配器 (Allocator) 也都满足可复制构造 (CopyConstructible) )
|
|
A a(b) | 构造满足 B(a)==b 和 A(b)==a 的 a 。不抛异常。(注意,这意味着所有因 rebind 相关联的分配器都彼此维护资源,例如内存池)
|
|
A a1(std::move(a))
A a1 = std::move(a) |
构造 a1 使之等于 a 之前的值。不抛异常。 a 的值不变且 a1 == a。 (C++17 起)
|
|
A a(std::move(b)) | 构造 a 使之等于 A(b) 之前的值。不抛异常。
|
|
a.construct(xptr, args) (可选) | 在先前分配的存储中由 xptr 所指向的地址上,以 args 为构造函数实参构造一个 X 类型的对象
|
|
a.destroy(xptr) (可选) | 析构 xptr 所指向的 X 类型的对象,但不解分配任何存储。
|
|
a.select_on_container_copy_construction() (可选) | 提供一个 A 实例,以被从当前正使用 a 的容器所复制构造的容器使用。通常要么返回 a 的副本,要么为默认构造的 A() 。
|
A
|
A::propagate_on_container_copy_assignment (可选) | 若在使用 A 类型的分配器的容器被复制赋值时需要复制该分配器,则为 true。注意,若源和目标容器的分配器比较不相等,则复制赋值时必须用旧分配器解分配目标的内存,再在进行各元素(和分配器)的复制之前用新分配器分配内存
|
std::true_type 或 std::false_type 或从它派生的类型 |
A::propagate_on_container_move_assignment (可选) | 若在使用 A 类型的分配器的容器被移动赋值时需要移动该分配器,则为 true。若此成员是 false 且源和目标容器的分配器比较不相等,则移动赋值不能夺取源内存的所有权,而必须逐个进行元素的移动赋值或移动构造,按需调整其自身内存的大小。
|
std::true_type 或 std::false_type 或从它派生的类型 |
A::propagate_on_container_swap (可选) | 若在使用 A 类型的分配器的两个容器进行交换时需要交换分配器,则为 true。若此成员是 false 且两个容器的分配器比较不相等,则容器交换的行为未定义。
|
std::true_type 或 std::false_type 或从它派生的类型 |
A::is_always_equal (C++17 起) (可选) | 若两个 A 类型的分配器始终比较相等则为 true。若不提供,则 std::allocator_traits 默认它等于 std::is_empty<A>::type
|
std::true_type 或 std::false_type 或从它派生的类型 |
注:
- ↑ 仅当此分配器是 SomeAllocator<T, Args> 形式的模板(其中
Args
是零或更多额外的模板类型形参)时,rebind 才是可选的(由 std::allocator_traits 提供)。
另外,为使类型
|
(C++17 起) |
给定
则,当且仅当 给定
则对于表达式 w1 == w2 与 w1 != w2,任一或两个对象都可由值等价的 给定
则,对于表达式 p1 == p2、p1 != p2、p1 < p2、p1 <= p2、p1 >= p2、p1 > p2、p1 - p2},任一或两个对象都可由值等价的 上述要求使得比较容器 (Container) 的 iterator 和 const_iterator 成为可能。 |
(C++14 起) |
分配器完整性要求若无论
|
(C++17 起) |
缀饰指针
当成员类型 pointer
不是原生指针时,它通常被称为“缀饰指针(fancy pointer)”。这种指针曾为支持分段内存架构而引入,并在当今用于访问在某些不同于原生指针所访问的同质虚拟地址空间的地址空间中所分配的对象。缀饰指针的一个实例是映射的不依赖地址指针 boost::interprocess::offset_ptr,它使得在共享内存和在每个进程中映射到不同地址的映射到内存文件中,分配 std::set 一类的基于结点的数据结构可行。通过类模板 std::pointer_traits,缀饰指针可以独立于提供它们的分配器而使用。
标准库
下列标准库组件满足分配器 (Allocator) 要求:
默认的分配器 (类模板) | |
(C++11) |
为多级容器实现的多级分配器 (类模板) |
(C++17) |
以 std::memory_resource 构造,支持基于它的运行时多态的分配器 (类模板) |
示例
一个 C++11 分配器,但添加了 [[nodiscard]] 以符合 C++20 风格。
#include <cstdlib> #include <new> template <class T> struct Mallocator { typedef T value_type; Mallocator() = default; template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {} [[nodiscard]] T* allocate(std::size_t n) { if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc(); if(auto p = static_cast<T*>(std::malloc(n*sizeof(T)))) return p; throw std::bad_alloc(); } void deallocate(T* p, std::size_t) noexcept { std::free(p); } }; template <class T, class U> bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; } template <class T, class U> bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }