zoukankan      html  css  js  c++  java
  • 整理 C++ 中 Allocator 的(几乎)所有细节 1

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

    必选成员

    Allocator 需要满足的条件有很多,但是大部分都是可选的,只有几个必须存在的成员。

    • 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。)

    • 拷贝赋值和移动赋值

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

    • 从另一个allocator类型构造

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

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

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

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

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

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

    一些可选成员的用途

    • 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

      当然以上只是标准容器的实现,你的容器大可以不必如此麻烦。

  • 相关阅读:
    2016.7.26
    2016.7.25
    2016.7.24
    C/C++基本数据类型所占字节数
    几个STL算法:includes,set_difference、set_intersection、set_symmetric_difference、set_union, pre_permutation, next_permutation
    h5 如何打包apk
    Ajax XMLHttpRequest对象的三个属性以及open和send方法
    twisted 使用
    python 的内建函数
    python中的 json 模块使用
  • 原文地址:https://www.cnblogs.com/pointer-smq/p/7522416.html
Copyright © 2011-2022 走看看