Is Stephen Lavavej's Mallocator the same in C+

2019-03-18 09:26发布

问题:

8 years ago, Stephen Lavavej published this blog post containing a simple allocator implementation, named the "Mallocator". Since then we've transitioned to the era of C++11 (and soon C++17) ... does the new language features and rules affect the Mallocator at all, or is it still relevant as is?

回答1:

STL himself has an answer to this question in his STL Features and Implementation techniques talk at CppCon 2014 (Starting at 26'30).

The slides are on github.

I merged the content of slides 28 and 29 below:

#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);
  }
};

Note that it handles correctly the possible overflow in allocate.



回答2:

As @kerrek suggested, here is a Mallocator that is based off the linked arena allocator with the arena part deleted.

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;
  }
};

Lots less code. Some traits for propogation.