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

Efficient usage of shared_ptr with custom allocator and deleter

本文关键字:shared ptr 有效 自定义 分配器 删除      更新时间:2023-10-16

目前我有以下代码用于使用自定义分配器和自定义删除器创建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中占用空间?

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