Возможен ли шаблон пула объектов для shared_ptr?

Можно ли создать пул объектов для shared_ptr?
Набросав это в своей голове, я вижу два способа сделать это, но у каждого есть недостаток:

  1. Если бы объекты T хранились в пуле многократного использования, то действие оборачивания T в shared_ptr по запросу get () привело бы к тому, что каждый раз блок управления перераспределялся в куче, что нарушало концепцию пула объектов.

  2. Если объекты shared_ptr были сохранены в пуле многократного использования, объект shared_ptr должен прекратить существование, чтобы инициировать пользовательское удаление, а пользовательская функция удаления вызывается только с указателем T. Так что нечего утилизировать.

0

Решение

После исчерпывающих исследований и испытаний я пришел к выводу, что есть нет законный способ (начиная с C ++ 11 или ниже) сделать пул объектов многоразового использования shared_ptr<T>это напрямую. Конечно, можно сделать пул T объекты довольно легко, что служит shared_ptr<T>да, но это приводит к выделению кучи с каждой подачей для блока управления.

Это является Однако возможно создать пул объектов shared_ptr<T>косвенно (а это только как я нашел, чтобы сделать это). Косвенным образом я имею в виду, что нужно реализовать собственный распределитель стилей «пул памяти» для хранения для повторного использования памяти, освобожденной shared_ptr<T> блоки управления уничтожены. Этот распределитель затем используется в качестве третьего параметра конструктора shared_ptr:

template< class Y, class Deleter, class Alloc >
std::shared_ptr( Y* ptr, Deleter d, Alloc alloc );

shared_ptr<T> будет по-прежнему создаваться / выделяться и удаляться / выделяться из памяти кучи — остановить ее невозможно — но, сделав память многократно используемой через пользовательский распределитель, можно добиться детерминированного использования памяти.

2

Другие решения

Да, это возможно. Но вместо того, чтобы вернуть ваш бассейн std::shared_ptr<T>Я бы подумал вернуть boost::intrusive_ptr<T>. Вы могли бы иметь intrusive_ptr_release() быть ответственным за освобождение этого блока из пула, и тогда только ваши пользователи должны создать T такой, что вы можете сделать intrusive_ptr<T>,

0

Вы можете хранить объекты в пуле (например, как unique_ptr). Пул возвращает shared_ptr по запросу. Пользовательский удалитель возвращает данные в пул. Простой пример этого приведен здесь:

#ifndef __POOL_H_
#define __POOL_H_

#include <list>
#include <mutex>
#include <algorithm>
#include <memory>
#include <stdexcept>

namespace common {

template<class T, bool grow_on_demand=true>
class Pool
{
public:
Pool(const char* name_p, size_t n)
: mutex_m(), free_m(0), used_m(0), name_m(name_p)
{
for (size_t i=0; i<n; i++)
{
free_m.push_front( std::make_unique<T>() );
}
}

const char* getName() const
{
return name_m.c_str();
}

std::shared_ptr<T> alloc()
{
std::unique_lock<std::mutex> lock(mutex_m);
if (free_m.empty() )
{
if constexpr (grow_on_demand)
{
free_m.push_front( std::make_unique<T>() );
}
else
{
throw std::bad_alloc();
}
}
auto it = free_m.begin();
std::shared_ptr<T> sptr( it->get(), [=](T* ptr){ this->free(ptr); } );
used_m.push_front(std::move(*it));
free_m.erase(it);
return sptr;
}size_t getFreeCount()
{
std::unique_lock<std::mutex> lock(mutex_m);
return free_m.size();
}

private:

void free(T *obj)
{
std::unique_lock<std::mutex> lock(mutex_m);
auto it = std::find_if(used_m.begin(), used_m.end(), [&](std::unique_ptr<T> &p){ return p.get()==obj; } );
if (it != used_m.end())
{
free_m.push_back(std::move(*it));
used_m.erase(it);
}
else
{
throw std::runtime_error("unexpected: unknown object freed.");
}
}

std::mutex mutex_m;
std::list<std::unique_ptr<T>> free_m;
std::list<std::unique_ptr<T>> used_m;
std::string name_m;
};

}

#endif /* __POOL_H_ */

По умолчанию пул добавляет новые элементы, если вы выделяете новый объект из пустого пула (grow_on_demand = true).

  • Новый Пул создает n элементы и добавляет их в пул (используя конструктор по умолчанию).
  • С mypool.alloc() Вы можете получить объект из бассейна.
  • Когда выделенный объект больше не используется, shared_ptr возвращается в пул автоматически (происходит неявно через [=](T* ptr){ this->free(ptr); } изнутри alloc(),
0
По вопросам рекламы [email protected]