如何防止在boost :: fast_pool_allocator管理的对象上调用析构函数?

时间:2014-04-08 16:00:13

标签: c++ c++11 boost pool

我想利用boost::fast_pool_allocator的以下广告功能(请参阅the Boost documentation for Boost Pool):

  

例如,您可能遇到要分配的情况   一堆小物体,然后到达你的一个点   程序,不再需要它们。使用池接口,   你可以选择运行他们的析构函数,或者只是将它们删除   遗忘 ...

(有关此报价,请参阅here。)

关键词是将其删除为遗忘。我不希望想要在这些对象上调用析构函数。

(原因是我有数以百万计的小对象,它们在堆上构成了一个极其复杂的所有权网络,当单个最单个对象关闭时,我的程序需要大约20分钟来调用所有析构函数我不需要调用这些析构函数,因为没有所需的副作用,所有内存都包含在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中的有希望的引用成为现实?

3 个答案:

答案 0 :(得分:3)

您将自定义分配器传递到std::map类。因此,此分配器将用于std::map内的所有内容:所有Obj数据以及std::map二叉树的节点。因此,如果您在purge_memory()的析构函数中调用Foo,那么所有这些内存都将失效,并在std::map析构函数中崩溃。

您假设自定义分配器负责对象&#39;取消分配不正确:它是std::map的任务,可以释放所有对象。因此,无论您是否通过自定义分配器,或者使用默认分配器,都将调用~Obj()

我没有看到任何优雅的解决方案。但这种方法应该有效:

  • 使用placement new从池的内存中创建Foo个对象,
  • 像往常一样使用此Foo对象,
  • 调用purge_memory()以释放池中的所有内存。不会调用任何析构函数~Obj~std::map

答案 1 :(得分:1)

默认情况下,池使用default_user_allocator_new_delete分配器。这将通过首先调用析构函数然后回收底层内存来销毁底层对象。 default_user_allocator_malloc_free将导致malloc内存被回收而不会触发析构函数 - 因此drop[ing] them off into oblivion

那就是说,如果你的树真的那么复杂,使用free而不是触发析构器似乎是一种非常好的方法来开始从你自己下面砍掉树枝并且可能开始泄漏你无法再触及的记忆。

答案 2 :(得分:0)

这个问题的答案包含在@ qehgt的回答下面的评论中,并在this new question中详细说明。

即:

对象实例化和删除的两个相关方面有明确和正式的区别:

  • (1)内存的分配和释放

  • (2)调用构造函数和析构函数

自定义分配器的目的只是(1)。

容器对象handle(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,则上述代码无效。有关详细信息,请参阅linked question