【问题标题】:Efficient usage of shared_ptr with custom allocator and deleter使用自定义分配器和删除器有效使用 shared_ptr
【发布时间】:2019-08-23 12:39:41
【问题描述】:

目前我有以下代码用于创建带有自定义分配器和自定义删除器的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>());
  }
}

我猜这种方法的问题是无状态的MPSingleDeleter 和MPAllocator 类在shared_ptr 结构中占用了一些空间。

有没有办法将自定义删除器和分配器传递给shared_ptr,而不会占用每个shared_ptr 中的空间?

【问题讨论】:

  • 我建议你自己制作MPSharedPtr,你可以硬编码它来使用你的东西,这样它就不会在每个实例中占用空间。

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


【解决方案1】:

你有点想在这里画圆:如果你想让你的 shared_ptr 保留你在构造函数中得到的任意删除器,在破坏时使用 - 你需要以某种方式将它存储在 shared_ptr 中;否则 - 删除时,您将无法选择使用每个类的默认值(例如静态)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多