aSt*_*eve 5 c++ boost memory-management c++11
场景:我有一个 G 类,它(通常)包含数千个从 N 类派生的类型的对象。所有这些对象都有一个明确定义的生命周期。首先,构造一个对象 G,然后添加 N 派生对象,然后用 G 完成一些计算,这不会改变 N 派生对象,然后 G 超出范围,并且随之而来的是, N 个派生对象。N 派生对象又包含指向添加到同一 G 对象的其他 N 派生对象的指针或指针的标准容器。G 表示具有异构节点的图。
我的目标是:
对我来说,这似乎需要多个池分配器 - 就像使用堆栈一样进行分配......并且仅在池被销毁时释放池分配。
我查看了 boost 池分配器 - 但没有找到为不同大小的异构对象建立多个独立池的方法。
接下来,我定义了自己的自定义分配器 - 但很快发现,虽然我可以将其作为模板参数传递给标准容器,例如 std::vector、std::set、std::list 等。- 允许我指定池分配器的类型...我感到困惑,因为我无法轻松指定两个独立的容器应该共享相同的(非全局)分配器池。我认识到一种解决方案是使用静态/全局并限制自己仅在一个线程中构造 G 对象。我还考虑过使用线程本地存储将自定义分配器与相关池关联起来......但认为这很难看。这两种方法都不直接支持同一线程中两个独立 G 对象的交错构造。
我是否忽略了 Boost 中问题的现有解决方案?
有没有比使用静态/全局或线程本地存储更好的习惯来实现我的目标?
更新
我已经阅读了 Stroustrup 的常见问题解答以及 boost::container 文档。起初,我对 Boost::container 感到非常鼓舞 - 但很失望没有看到如何在这些容器中使用有状态分配器的具体示例。我已经能够简化我原来要问的问题......给定一个结构:
struct DataUnit { map<int,string> m; list<int> l; }
Run Code Online (Sandbox Code Playgroud)
如何确保对于 DataUnit 的每个实例,都有一个池来分配 m 和 l 的内部组成部分?如果我将自定义分配器传递给映射和列表,则 m 和 l 会获得此容器的独立实例。我最初以为我可以使用 get_allocator() 用我的 aerena/pool 初始化分配器...但是,遗憾的是,在 vector<...> 的默认构造函数返回之前调用 allocate() ..所以我不能那样做。
更奇怪的是,我发现,在涉足 boost::container 一段时间之后……普通的 std 容器有一个 get_allocator() (在 MSVC 2010 和 2012 以及 g++ 4.6.3 上),这表明这些标准库上下文具有与 boost::container 类似的有状态分配器支持。
不幸的是,我仍然没有可行的解决方案来解决我原来的问题(尽管我现在可以更雄辩地表达它。)
更新2
谢谢,pmr,你最后的评论是我授予“正确答案”的东西 - 如果你将其归类为答案。:) 我的问题是,找到 boost::container 后,我期望它的文档能够明确说明任何新功能 - 例如在构造时传递分配器对象...并且我没有检查 boost::container 源代码正确编码。我现在确信,Boost::container 为我上述所有问题提供了一个非常优雅且直观的(如果记录不完整)解决方案。再次感谢!
警告:完全未经测试的代码。我不知道它是什么“惯用语” - 但下面的 1.5 页代码应该可以解决您的问题。
class GraphNodeAllocator
{
struct CMemChunk
{
CMemChunk* pNext;
BYTE* data()
{
return static_cast<BYTE*>( static_cast<void*>( this + 1 ) );
}
};
CMemChunk* m_pChunk; // Most recently allocated a.k.a. current chunk
BYTE* m_pFirstByte; // First free data byte within the current chunk
size_t m_freeBytes; // Count of free bytes within the current chunk
static const size_t cbChunkAlloc = 0x10000; // 65536 bytes per single allocation
static const size_t cbChunkPayload = cbChunkAlloc - sizeof( CMemChunk );
void* Allocate( size_t sz )
{
if( sz > cbChunkPayload )
return NULL;
if( m_freeBytes >= sz )
{
// Current chunk has the space
m_freeBytes -= sz;
void* res = m_pFirstByte;
m_pFirstByte += sz;
return res;
}
// Need a new chunk
CMemChunk* pChunk = static_cast< CMemChunk* >( malloc( cbChunkAlloc ) );
if( NULL == pChunk )
return NULL;
pChunk->pNext = m_pChunk;
m_pChunk = pChunk;
m_pFirstByte = m_pChunk->data();
m_freeBytes = cbChunkPayload;
return Allocate( sz );
}
public:
inline GraphNodeAllocator(): m_pChunk( NULL ), m_pFirstByte( NULL ), m_freeBytes( 0 ) { }
inline ~GraphNodeAllocator()
{
while( NULL != m_pChunk )
{
CMemChunk* pNext;
pNext = m_pChunk->pNext;
free( m_pChunk );
m_pChunk = pNext;
}
}
template<typename E>
inline E* newNode()
{
void* ptr = Allocate( sizeof( E ) );
if( NULL == ptr ) return NULL;
return ::new( ptr ) E();
}
};
Run Code Online (Sandbox Code Playgroud)
PS 这个想法是从 Microsoft 的 CAtlPlex 类借用的,这是大多数 Microsoft 模板容器(列表、映射、哈希图)通常比 STL 对应项快 2 倍的首要原因。自从我放弃使用 std::vector、std::set、std::list 等而转而使用 ATL 的等价物后,我变得更加快乐。