Как предотвратить вызов деструкторов для объектов, управляемых boost :: fast_pool_allocator?

Я хотел бы воспользоваться следующей рекламируемой функцией boost::fast_pool_allocator (увидеть документация Boost для Boost Pool):

Например, у вас может возникнуть ситуация, когда вы хотите выделить
куча мелких предметов в одной точке, а затем достичь точки в вашем
программа, где ни один из них больше не нужен. Использование интерфейсов пула,
вы можете запустить их деструкторы или просто бросить их в
забвение

(Увидеть Вот за эту цитату.)

Ключевая фраза бросить их в забвение. я не делайте хочу, чтобы деструкторы вызывали эти объекты.

(Причина в том, что у меня есть миллионы крошечных объектов, которые образуют чрезвычайно сложную сеть владения в куче, и моей программе требуется около 20 минут для вызова всех деструкторов, когда единственный родительский объект выходит из стека. I не нужно называть эти деструкторы, потому что нет желаемых побочных эффектов, и вся память содержится в boost::pool.)

К сожалению, несмотря на обещание вышеуказанной документации и обещание boost::pool Я не могу найти способ предотвратить вызов деструкторов управляемых объектов.

Проблема легко изолируется в небольшом примере программы:

class Obj
{
public:
~Obj()
{
// Placing a breakpoint here indicates that this is *always* reached
// (except for the crash scenario discussed below)
int m = 0;
}
};

typedef std::map<int, Obj, std::less<int>,
boost::fast_pool_allocator<std::pair<int const, Obj>>>
fast_int_to_int_map;

class Foo
{
public:
~Foo()
{
// When the following line is uncommented, the program CRASHES
// when the destructor is exited - because the Obj destructors
// are called on the invalid Obj ghost instances

//boost::singleton_pool<boost::fast_pool_allocator_tag,
//                  sizeof(std::pair<int const, Obj>)>::purge_memory();
}

fast_int_to_int_map mmap;
};

void mfoo()
{
// When this function exits, the Foo instance goes off the stack
// and its destructor is called, in turn calling the destructors
// of the Obj instances - this is NOT desired!

Foo foo;
foo.mmap[0] = Obj();
foo.mmap[1] = Obj();
}

int main()
{
mfoo();

// The following line deallocates the memory of the pool just fine -
// but does nothing to prevent the destructors of the Obj instances
// from being called

boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(std::pair<int const, Obj>)>::purge_memory();
}

Как отмечено в комментариях к коду, деструкторы Obj экземпляры, которые управляются boost::pool всегда называются.

Что я могу сделать, чтобы сделать многообещающую цитату из документации Boost Pool, drop them off into oblivion, сбываться?

4

Решение

Вы передаете пользовательский распределитель в свой std::map учебный класс. Таким образом, этот распределитель будет использоваться для всего внутри std::map: для всех Obj данные, а также для узлов двоичного дерева std::map, В результате, если вы позвоните purge_memory() в Fooдеструктор, то вся эта память становится недействительной, и она падает в std::map деструктор.

Ваше предположение, что пользовательский распределитель отвечает за перераспределение объектов, неверно: это std::mapЗадача освободить все объекты. Так, ~Obj() будет вызываться независимо от того, передадите ли вы пользовательский распределитель или используете ли вы по умолчанию.

Я не вижу элегантного решения для вашего вопроса. Но этот подход должен работать:

  • использование placement new создавать Foo объект из памяти пула,
  • использовать этот Foo возражать как обычно,
  • вызов purge_memory() освободить всю память из пула. Нет деструкторов ~Obj или же ~std::map будет называться.
3

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

По умолчанию пул использует default_user_allocator_new_delete Распределитель. Это уничтожит лежащие в основе объекты, сначала вызвав деструктор, а затем освободив основную память. default_user_allocator_malloc_free приведет к тому, что malloc’ed память будет восстановлена ​​без запуска деструктора — следовательно drop[ing] them off into oblivion,

Тем не менее, если ваше дерево действительно настолько сложное, использование бесплатного вместо запуска деструкторов кажется действительно хорошим способом начать вырубать ветки из-под себя и потенциально начинать утечку памяти, которую вы больше не можете получить.

1

Ответ на этот вопрос содержится в комментариях под ответом @ qehgt, а также подробно в этот новый вопрос.

А именно:

Существует четкое и формальное различие между двумя взаимосвязанными аспектами создания и удаления объектов:

  • (1) Распределение и освобождение памяти

  • (2) Вызов конструкторов и деструкторов

Назначение пользовательского распределителя — только (1).

Контейнерные объекты обрабатывают (2), и они вызывают функции в пользовательском распределителе, чтобы определить расположение памяти, в которой должен создаваться объект, и сообщить пользовательскому распределителю, что все в порядке, чтобы освободить выделенную память для заданных объектов. Но вызовы самого конструктора / деструктора выполняются контейнером, а не пользовательским распределителем.

Поэтому способ достижения цели этого вопроса заключается в следующем: объявите объект контейнера с помощью new и никогда не звонить delete на нем (но используйте пользовательский распределитель, чтобы гарантировать, что объекты, которые вы позже создадите в контейнере, будут храниться в пуле памяти, а затем вручную освободить пул памяти, вызвав purge_memory()):

class Obj { // has operator<() for use with std::set };

typedef std::set<Obj, std::less<Obj>, boost::fast_pool_allocator<Obj>> fast_set_obj;

// Deliberately do not use a managed pointer -
// I will *NOT* delete this object, but instead
// I will manage the memory using the memory pool!!!
fast_set_obj * mset = new fast_set_obj;

// ... add some Obj's to 'mset'
mset->insert(Obj());
mset->insert(Obj());

// Do something desireable with the set ...
...

// All done.
// It's time to release the memory, but do NOT call any Obj destructors.
// The following line of code works exactly as intended.

boost::singleton_pool<boost::fast_pool_allocator_tag, sizeof(Obj const)>::purge_memory();

(Приведенный выше код взят из связанного вопроса выше.)

Тем не менее, у меня все еще есть проблема, не связанная напрямую с намерением, стоящим за этим текущим вопросом: приведенный выше код не работает, если map используется вместо set, Увидеть связанный вопрос для деталей.

0
По вопросам рекламы [email protected]