通过自定义分配器和删除器有效使用shared_ptr

时间:2019-08-23 12:39:41

标签: c++ optimization memory c++17 shared-ptr

目前,我有以下代码用于使用自定义分配器和自定义删除器创建shared_ptr(请参见底部的要点,而依赖项定义位于顶部):

template <typename T>
class MPAllocator {
public:
  //// The following will be the same for virtually all allocators.
  typedef T * pointer;
  typedef const T * const_pointer;
  typedef T& reference;
  typedef const T& const_reference;
  typedef T value_type;
  typedef std::size_t size_type;
  typedef ptrdiff_t difference_type;

  typedef std::true_type propagate_on_container_move_assignment;
  //TODO: how shall the below be?
  //typedef std::true_type propagate_on_container_copy_assignment;
  //typedef std::true_type propagate_on_container_swap;

  T * address(T& r) const noexcept {
    return &r;
  }

  const T * address(const T& s) const noexcept {
    return &s;
  }

  std::size_t max_size() const noexcept {
    // The following has been carefully written to be independent of the definition of size_t and to avoid
    // signed/unsigned warnings.
    return (static_cast<std::size_t>(0) - static_cast<std::size_t>(1)) / sizeof(T);
  }

  //// The following must be the same for all allocators.
  template <typename U>
  struct rebind {
    typedef MPAllocator<U> other;
  };

  template <class U, class ...Args> void construct(U* p, Args&&... args) const {
    void * const pv = static_cast<void *>(p);
    ::new (pv) U(std::forward<Args>(args)...);
  }

  void destroy(T * const p) const {
    p->~T();
  }

  // Default constructor, copy constructor, rebinding constructor, and destructor.
  // Empty for stateless allocators.
  MPAllocator() noexcept { }

  // See https://stackoverflow.com/questions/54050890/how-to-utilize-template-copymove-constructor-and-assignment-operator
  MPAllocator(const MPAllocator&) noexcept { }

  template <typename U> MPAllocator(const MPAllocator<U>&) noexcept { }

  ~MPAllocator() noexcept { }


  // The following will be different for each allocator.
  T * allocate(const std::size_t n) const {
    // The return value of allocate(0) is unspecified. Mallocator returns NULL in order to avoid depending on
    // malloc(0)'s implementation-defined behavior (the implementation can define malloc(0) to return NULL, in which
    // case the bad_alloc check below would fire). All allocators can return NULL in this case.
    if (n == 0) {
      return NULL;
    }

    // All allocators should contain an integer overflow check. The Standardization Committee recommends that
    // std::length_error be thrown in the case of integer overflow.
    if (n > max_size()) {
      throw std::length_error("MPAllocator<T>::allocate() - Integer overflow.");
    }

    void * const pv = MemPool::Instance().Acquire(n * sizeof(T));

    // Allocators should throw std::bad_alloc in the case of memory allocation failure.
    if (pv == NULL) {
      throw std::bad_alloc();
    }

    return static_cast<T*>(pv);
  }

  void deallocate(T * const p, const std::size_t n) const {
    //TODO: check that the destructors of T get called, see http://www.cplusplus.com/reference/memory/allocator/deallocate/
    MemPool::Instance().Release(p, n * sizeof(T));
  }


  // The following will be the same for all allocators that ignore hints.
  template <typename U>
  T * allocate(const std::size_t n, const U * /* const hint */) const {
    return allocate(n);
  }
};

template <typename T, typename U> inline
bool operator== (const MPAllocator<T>&, const MPAllocator<U>&) noexcept {
  return true; // So long as our allocator doesn't have a state
}

template <typename T, typename U> inline
bool operator!= (const MPAllocator<T>&, const MPAllocator<U>&) noexcept {
  return false;
}

template<typename T> struct MPSingleDeleter {
  void operator()(T *p) {
    if (p != nullptr) {
      p->~T();
      MemPool::Instance().Release(p, sizeof(T));
    }
  }
};

struct MemHelper {
  template<typename T, typename ...Args>  static T* NewSingle(Args&&... args) {
    void * const p = MemPool::Instance().Acquire(sizeof(T));
    ::new (p) T(std::forward<Args>(args)...);
    return static_cast<T*>(p);
  }

  // Make SSP="Single Shared Pointer"
  template<typename T, typename ...Args> static std::shared_ptr<T> MakeSSP(Args&&... args) {
    return std::shared_ptr<T>(NewSingle<T>(std::forward<Args>(args)...), MPSingleDeleter<T>(), MPAllocator<T>());
  }
}

这种方法的问题是,无状态的MPSingleDeleterMPAllocator类在shared_ptr结构内部占用了一些空间。

有没有一种方法可以将自定义删除器和分配器传递给shared_ptr,而又不占用每个shared_ptr的空间?

1 个答案:

答案 0 :(得分:3)

您正在尝试在此处对圆进行平方处理:如果您希望shared_ptr抓住构造函数中使用的任意删除器,则在销毁时使用它-您需要将其存储在{{ 1}}以某种方式;否则-删除时,您别无选择地使用每个类别的默认值(例如静态值)。