C++ 概念: Allocator

来自cppreference.com
< cpp‎ | concept
 
 
 

封装访问/寻址、分配/解分配及对象的构造/析构策略。

每个可能需要分配或释放内存的标准库组件,从 std::stringstd::vector 及每个 std::array 外的容器,到 std::shared_ptrstd::function ,都通过分配器Allocator)进行:分配器是满足下列要求的类类型对象。

某些要求是可选的:模板 std::allocator_traits 为所有可选要求提供默认实现,且所有标准库容器和其他具分配器类不是直接,而是通过 std::allocator_traits 访问分配器。

目录

[编辑] 要求

给定

  • T ,无 cv 限定对象类型
  • A ,对于类型 T 的分配器(Allocator)类型
  • aA 类型对象
  • B ,对于某无 cv 限定对象类型 U 的对应分配器(Allocator)类型(重绑定 A 所得)
  • ptrallocator_traits<A>::pointer 类型的值,由调用 allocator_traits<A>::allocate() 获得
  • cptrallocator_traits<A>::const_pointer 类型的值,从 ptr 转换获得
  • vptrallocator_traits<A>::void_pointer 类型的值,从 ptr 转换获得
  • cvptrallocator_traits<A>::const_void_pointer 类型的值,从 cptrvptr 转换获得
  • xptr ,对于某无 cv 限定对象类型 X 的可解引用指针,
  • r ,从表达式 *ptr 获得的 T 类型左值
  • nallocator_traits<A>::size_type 类型的值
表达式 要求 返回类型
A::pointer (可选) 满足可空指针NullablePointer)、随机访问迭代器RandomAccessIterator)及连续迭代器ContiguousIterator)(见后述缀饰指针
A::const_pointer (可选) A::pointer 可转换为 A::const_pointer可空指针NullablePointer)、随机访问迭代器RandomAccessIterator)及连续迭代器ContiguousIterator
A::void_pointer (可选) A::pointer 可转换为 A::void_pointer

B::void_pointerA::void_pointer 是同一类型。满足可空指针NullablePointer

A::const_void_pointer (可选) A::pointerA::const_pointerA::void_pointer 可转换到 A::const_void_pointer

B::const_void_pointerA::const_void_pointer 是同一类型。满足可空指针NullablePointer

A::value_type 类型 T
A::size_type (可选) A::size_type 能表示能分配的最大对象 A 的大小 无符号整数类型
A::difference_type (可选) A::difference_type 能表示指向 A 所分配对象的二个指针的差 有符号整数类型
A::template rebind<U>::other (可选[1]) 对于任何 UB::template rebind<T>::otherA 类型 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>(vptr) == cptr A::const_pointer
std::pointer_traits<A::pointer>::pointer_to(r) A::pointer
a.allocate(n) 分配适合 nT 类型对象的存储,但不构造它们。可以抛异常。 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)==bA(b)==aa 。不抛异常。(注意,这隐含所有 rebind 所关联的分配器维护彼此的资源,例如内存池)
A a1(std::move(a))

A a1 = std::move(a)

构造 a1 使之等于 a 之前的值。不抛异常。
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_typestd::false_type 或自之导出的类型
A::propagate_on_container_move_assignment (可选) 若类型 A 的分配器在使用它的容器被移动赋值时需要移动则为 true 。若此成员是 false 且源和目标容器的分配器比较不相等,则移动赋值不能夺取源内存的所有权,而必须逐个元素移动赋值或移动构造,按需调整其自身内存的大小。 std::true_typestd::false_type 或自之导出的类型
A::propagate_on_container_swap (可选) 若类型 A 的分配器在使用它们的容器交换时需要交换则为 true 。若此成员是 false 且二个容器的分配器比较不相等,则容器交换的行为未定义。 std::true_typestd::false_type 或自之导出的类型
A::is_always_equal (C++17 起) (可选) 若二个类型 A 的分配器始终比较相等则为 true 。若不提供,则 std::allocator_traits 默认它等于 std::is_empty<A>::type std::true_typestd::false_type 或自之导出的类型

注意: [1] 若此分配器是 SomeAllocator<T, Args> 形式的模板,其中 Args 是零或更多可选的模板参数,则 rebind 只是可选的(为 std::allocator_traits 所提供)。

另外,为令类型 A 满足分配器Allocator

  • A::propagate_on_container_copy_assignment::valuetrue ,则 A 必须满足可复制赋值CopyAssignable)且复制操作必须不抛异常
  • A::propagate_on_container_move_assignment::valuetrue ,则 A 必须满足 可移动赋值MoveAssignable)且移动操作必须不抛异常。
  • A::propagate_on_container_swap::valuetrue ,则 A 的左值必须可交换Swappable)且交换操作必须不抛异常
(C++17 起)

给定

  • X::void_pointerX::const_void_pointerX::pointerX::const_pointer 类型(可以不同)的对象 x1x2

则,若 x1x2 都能用只使用这四种类型的 static_casts 序列分别显式转换为 X::const_pointer 类型对象 px1px2 ,且表达式 px1 == px2 求值为 true ,则 x1 与 x2 是等价值的指针值

给定

  • X::void_pointer 类型对象 w1w2

则对于表达式 w1 == w2w1 != w2 ,一或两个对象能为等价值X::const_void_pointer 类型对象所替换,而无语义更改。

给定

  • X::pointer 类型对象 p1p2

则,对于表达式 p1 == p2p1 != p2p1 < p2p1 <= p2p1 >= p2p1 > p2p1 - p2} ,一或二个对象能为等价值X::const_pointer 类型对象替换而无语义更改。

上述要求使得能比较容器Container)的 iterators 和 const_iterator 。

(C++14 起)


分配器完整性要求

若下列二者皆真,无论 T 是否完整类型,则分配器类型 X 对类型 T 额外满足分配器完整性要求

(C++17 起)

[编辑] 缀饰指针

当成员类型 pointer 不是裸指针时,它通常代表一个“缀饰指针”。这种指针曾为支持分段内存架构引入,并在当今用于访问分配在一些地址空间的对象,这些地址空间异于裸指针所访问的同态虚拟地址。缀饰指针的实例是映射的不依赖地址指针 boost::interprocess::offset_ptr ,它使得在共享内存和在每个进程中映射到不同地址的映射到内存文件中,分配 std::set 一类的基于结点的数据结构可行。缀饰指针可以独立于提供它们的分配器使用,通过类模板 std::pointer_traits

[编辑] 标准库

下列标准库组件满足分配器Allocator)概念:

默认的分配器
(类模板) [编辑]
为多级容器实现的多级分配器
(类模板) [编辑]
std::memory_resource 构造,支持基于它的运行时多态的分配器
(类模板) [编辑]

[编辑] 示例

最小的 C++11 分配器

#include <cstdlib>
template <class T>
struct Mallocator {
  typedef T value_type;
  Mallocator() = default;
  template <class U> Mallocator(const Mallocator<U>&) {}
  T* allocate(std::size_t n) { return static_cast<T*>(std::malloc(n*sizeof(T))); }
  void deallocate(T* p, std::size_t) { 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; }