How to prevent destructors from being called on objects managed by boost::fast_pool_allocator?

后端 未结 3 561
伪装坚强ぢ
伪装坚强ぢ 2021-01-06 09:33

I would like to take advantage of the following advertised feature of boost::fast_pool_allocator (see the Boost documentation for Boost Pool):

相关标签:
3条回答
  • 2021-01-06 10:03

    The answer to this question is contained in the comments beneath @qehgt's answer, and in detail in this new question.

    Namely:

    There is a clear and formal distinction between two related aspects of object instantiation and deletion:

    • (1) Allocation and deallocation of memory

    • (2) Calling of constructors and destructors

    The purpose of a custom allocator is (1) only.

    The container objects handle (2), and they call functions in the custom allocator to determine the location of memory in which to construct the object, and to tell the custom allocator that it's OK to free the allocated memory for given objects. But the calls to the constructor/destructor themselves are made by the container, not by the custom allocator.

    Therefore, the way to achieve the goal of this question is the following: Declare the container object via new and NEVER CALL delete on it (but use the custom allocator to guarantee that the objects you later create in the container are stored in the memory pool, and then manually free the memory pool yourself by calling 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();
    

    (The above code is taken from the linked question, above.)

    However, I am still having an issue, not directly related to the intention behind this current question: The code above does not work if a map is used instead of a set. See the linked question for details.

    0 讨论(0)
  • 2021-01-06 10:08

    You pass the custom allocator into your std::map class. So, this allocator will be used for everything inside of std::map: for all Obj data and also for nodes of a binary tree of std::map. As the result, if you call purge_memory() in Foo's destructor then all this memory becomes invalid, and it crashes in std::map destructor.

    Your assumption that a custom allocator is responsible for objects' de-allocation is not correct: it's a std::map's task to free all objects. So, ~Obj() will be called regardless if you pass the custom allocator or if you use a default one.

    I don't see any elegant solution for your question. But this approach should work:

    • use placement new to create Foo object from pool's memory,
    • use this Foo object as usual,
    • call purge_memory() to release all memory from the pool. No destructors ~Obj or ~std::map will be called.
    0 讨论(0)
  • By default pool uses the default_user_allocator_new_delete allocator. This will destroy underlying objects by calling the destructor first and then reclaiming underlying memory. The default_user_allocator_malloc_free will cause malloc'ed memory to be reclaimed without firing the destructor - hence drop[ing] them off into oblivion.

    That said, if your tree is really that complicated, using free instead of firing destructors seems like a really good way to start chopping branches out from under yourself and potentially start leaking memory you can no longer reach.

    0 讨论(0)
提交回复
热议问题