问题 Stephen Lavavej的Mallocator在C ++ 11中是一样的吗?


8年前,斯蒂芬拉瓦维出版 这篇博文 包含一个简单的allocator实现,名为“Mallocator”。从那时起,我们已经过渡到C ++ 11时代(很快就会出现C ++ 17)......新的语言特性和规则是否会影响Mallocator,或者它仍然是相关的?


9801
2018-04-09 14:03


起源

它现在简单得多了。 - Kerrek SB
@KerrekSB:链接?说明? - einpoklum
也许看看分配器示例中途向下 本指南 并取代所有提及的“竞技场” malloc/free... - Kerrek SB
@KerrekSB:所以,没有了 construct()? - einpoklum
没有, construct 现在有一个默认实现作为分配器特征的一部分。如果你需要它来做非默认的事情,你可以指定它(例如 scoped_allocator 一样)。 - Kerrek SB


答案:


STL本人对他的这个问题有了答案 STL功能和实现技术 在CppCon 2014上讲话(从26'30开始)。

幻灯片 在github上。

我合并了以下幻灯片28和29的内容:

#include <stdlib.h> // size_t, malloc, free
#include <new> // bad_alloc, bad_array_new_length
template <class T> struct Mallocator {
  typedef T value_type;
  Mallocator() noexcept { } // default ctor not required
  template <class U> Mallocator(const Mallocator<U>&) noexcept { }
  template <class U> bool operator==(
    const Mallocator<U>&) const noexcept { return true; }
  template <class U> bool operator!=(
    const Mallocator<U>&) const noexcept { return false; }

  T * allocate(const size_t n) const {
      if (n == 0) { return nullptr; }
      if (n > static_cast<size_t>(-1) / sizeof(T)) {
          throw std::bad_array_new_length();
      }
      void * const pv = malloc(n * sizeof(T));
      if (!pv) { throw std::bad_alloc(); }
      return static_cast<T *>(pv);
  }
  void deallocate(T * const p, size_t) const noexcept {
      free(p);
  }
};

请注意,它正确处理分配中可能出现的溢出。


9
2018-04-09 19:46



它应该是 std::numeric_limits<size_t>::max() 而不是 static_cast<size_t>(-1) 虽然? - einpoklum
@einpoklum标准保证相同:size_t必须遵守mod 2 ^ N算术,其中N是size_t的位数。 - Arnaud
使用后者而不是前者编码恕我直言仍然是不好的。 - einpoklum
@einpoklum我同意。 - Arnaud
这将与std :: list一起使用吗? - In78


正如@kerrek建议的那样,这里有一个基于的Mallocator 关联 竞技场分配器与竞技场部分删除。

template<class T>
struct Mallocator11 {
  using value_type = T;
  using pointer = T*;
  using propagate_on_container_copy_assignment = std::true_type;
  using propagate_on_container_move_assignment = std::true_type;
  using propagate_on_container_swap = std::true_type;

  Mallocator11(Mallocator11 const&) = default;
  Mallocator11& operator=(Mallocator11 const&) = default;
  Mallocator11()=default;
  template<class U>
  Mallocator11(Mallocator11<U> const&) noexcept {}
  template<class U>
  Mallocator11& operator=(Mallocator11<U> const&) noexcept {return *this}


  pointer allocate(std::size_t n) {
    if (std::size_t(-1) / sizeof(T) < n)
      throw std::bad_array_new_length(); // or something else
    if (!n) return nullptr; // zero means null, not throw
    if(auto*r= static_cast<pointer>(malloc(n * sizeof(T))))
      return r;
    throw std::bad_alloc();
  }
  void deallocate(pointer p, std::size_t n) {
    free(p);
  }
  template<class U>
  bool operator==(Mallocator11<U> const& rhs) const {
    return true;
  }
  template<class U>
  bool operator!=(Mallocator11<U> const& rhs) const {
    return false;
  }
};

代码少了很多。传播的一些特征。


2
2018-04-09 15:32



您能否详细说明溢出检测以及何时可能需要或不需要? - einpoklum
@KerrekSB尚未。一些容器要求仍以POCMA表示。与此同时, is_always_equal 对于空分配器已经是正确的。 - T.C.
@Arnaud“可以有意义地传递给的最大值 allocate“与”可以传递以分配而不会导致异常的最大值是不一样的。“这些要求字面上说明了这一点 allocate(max_size() + 2) 没有任何意义。 - Casey
@Casey公平,如果 max_size() 是一个 size_t,和 max_size()+2 碰巧等于1,那它有意义(size_t 某些n)是mod-2 ^ n。不是 有用 意思,但因为 max_size()+2<max_size() 它有意义。等等,这不公平:这是愚蠢的迂腐。我知道这是两个中的一个。有时很难说。 ;) - Yakk - Adam Nevraumont
@Elkvis没有; size_t保证无符号,无符号保证是数学模型 2^n 对于一些 n。从而 unsigned_type(-1) 是 总是 任何无符号类型的最大值。作为一个小小的奖励,它不需要包括具有的标准头文件 numeric_limits 在里面。 - Yakk - Adam Nevraumont