整理 C++ 中 Allocator 的(几乎)全部细节 1

Allocator(概念)是对访问、寻址、分配、释放、构造和析构策略的封装。是一个知足特定要求的类。标准库中须要分配释放存储空间的容器都须要一个Allocator,除了std::array。函数

必选成员

Allocator 须要知足的条件有不少,可是大部分都是可选的,只有几个必须存在的成员。spa

  • value_type: 要分配空间的类型
  • allocate(n): 分配方法
  • deallocate(ptr, n): 释放方法
  • 拷贝构造:

    对于表达式Alloc a2 = a1; Alloc a2(a1) 要求执行完毕后a2 == a1,不可抛出异常指针

  • 移动构造:

    对于表达式Alloc a2 = std::move(a1); Alloc a2(std::move(a1)); 使用a1构造a2,a2应等于a1的先前值。(C++17 起要求a1的值在构造后不发生改变,而且a1 == a2。)code

  • 拷贝赋值和移动赋值

    标准没有声明他们的存在,可是显然他们应该和上面的构造语义相同。对象

  • 从另外一个allocator类型构造

    用于map等分配的实际类型不是你传入的allocator的value_type的状况。内存

    对于表达式 AllocA a(b),其中b是由AllocA::template rebind得到的类型AllocB的实例,构造a,使得AllocB(a) == b, b == AllocA(a)。ci

  • 从另外一个allocator类型移动构造

    对于表达式 AllocA a(b),其中b是由AllocA::template rebind得到的类型AllocB的实例,构造a,使得a == 以前的 AllocA(b)get

其它的可选设施能够在这一页找到,是否实现由 Allocator 的实现逻辑决定 http://en.cppreference.com/w/cpp/concept/Allocatorit

标准推荐经过 std::allocator_traits<Allocator> 来调用 Allocator 的各类方法,及获取其余类型,这个 trait 类提供了 Allocator 中可选成员的默认实现。io

一些可选成员的用途

  • size_type

    若是你不想使用默认的std::size_t,能够用这个来自定义size_type类型,固然对应的allocate和deallocate方法的size参数也应该变成这个类型

  • template rebind<U>::other

    rebind用来从已有的allocator类型获取一个新的用来分配另外一个类型U的allocator类型

    注意,rebind只对有模板参数的allocator可选

    allocator_traits的默认实现是用U来替换当前类型的第一个模板参数

  • allocate(n, ptr)

    分配足够容纳n个对象的连续空间,ptr用做一个hint(好比在ptr地址附近寻找可用内存,用来保持局部性)

  • max_size()

    获取可分配的最大对象数目

    allocator_traits会提供一个返回(size_t)-1的实现(或者(size_t)-1 / sizeof(T),since C++17)

  • select_on_container_copy_construction()

    在标准库容器拷贝构造时,由构造函数调用,向源allocator获取一个用来构造新容器的allocator的实例

    allocator_traits会提供一个直接返回源容器的allocator自己的实现

    在构造函数不可以知足allocator的逻辑需求时定义这个函数

  • construct(ptr, args…)

    在给定指针指向的内存上构造对象,须要注意的是,ptr指向的对象类型不必定是allocator的value_type,这个函数有必要作成模板的

    在须要自定义对象构造行为时定义它,好比打个log,try_catch一下什么的

  • destroy(ptr)

    析构ptr指向的对象,须要注意的是,ptr指向的对象类型也不必定是value_type,这个函数有必要作成模板的

  • is_always_equal (since C++17)

    allocator的相等比较的意义是,一个allocator分配的空间,是否能够用另一个allocator来释放,is_always_equal旨在尽量消除运行期的比较

    std::allocator就是always_equal的,由于他们都是new和delete的封装,一个std::allocator new的固然能够用另外一个std::allocator来delete

    allocator_traits的默认实现是,当你的allocator是空类,那么为true_type

  • propagate_on_container_copy_assignment
  • propagate_on_container_move_assignment
  • propagate_on_container_swap

    此三个类型标记了在容器进行拷贝赋值、移动赋值或交换的时候,allocator是否须要进行对应操做。

    容器在进行拷贝赋值、移动赋值和交换时的逻辑,应该考虑到以上成员和allocator的相等性

    在两个容器拷贝赋值时(container1 = container2)

    propagate…copy…

    两个allocator是否相等

    拷贝赋值行为

    true

    true

    拷贝allocator,拷贝container2全部元素

    true

    false

    析构container1全部元素并释放空间,拷贝allocator,拷贝container2元素

    false

    true

    不拷贝allocator,拷贝container2全部元素

    false

    false

    不拷贝allocator,拷贝container2全部元素

    在两个容器移动赋值时(container1 = std::move(container2))

    propagate…move…

    两个allocator是否相等

    移动赋值行为

    true

    true

    析构container1全部元素并释放空间,移动allocator,接管container2的内部指针

    true

    false

    析构container1全部元素并释放空间,移动allocator,接管container2的内部指针

    false

    true

    析构container1全部元素并释放空间,不移动allocator,接管container2的内部指针

    false

    false

    析构container1全部元素,不释放空间,不移动allocator,分配足够装下container2全部元素的空间,将container2的元素尽数移动过来

    在两个容器交换时,没有更多问题,仅需视propagate_on_container_swap值,交换allocator便可。但须要注意的是,若是allocator不可交换,而且不相等,那么容器交换是UB

    固然以上只是标准容器的实现,你的容器大能够没必要如此麻烦。

相关文章
相关标签/搜索