场景:我有一个G类,它包含(通常)数千个从类N派生的类型的对象。所有这些对象都有明确定义的生命周期。首先,构造一个对象G,然后添加N个派生的对象,然后用G完成一些计算,这不会改变N个派生的对象,然后G超出范围,并且随之而来的是成分N个派生的对象。 N派生对象又包含指向添加到同一G对象的其他N派生对象的指针或标准容器。 G表示具有异构节点的图。
我的目标是:
对我来说,这似乎要求多个池分配器 - 分配好像使用堆栈......并且只在池被销毁时释放池化分配。
我查看了boost池分配器 - 但没有找到为不同大小的异构对象建立多个独立池的方法。
接下来,我定义了自己的自定义分配器 - 但很快发现,虽然我可以将它作为模板参数传递给标准容器,如std :: vector,std :: set,std :: list等。 - 允许我指定池分配器的类型...我来 - unstuck因为我不能轻易指定两个独立的容器应该共享相同的(非全局)分配器池。我认识到一种解决方案是使用静态/全局并限制自己仅在一个线程中构造G对象。我还考虑过使用线程本地存储将自定义分配器与相关池相关联......但认为这很难看。这两种方法都没有直接支持同一线程中两个独立G对象的交错构造。
我是否忽略了Boost中我的问题的现有解决方案?
有没有比使用静态/全局或线程本地存储来实现我的目标更好的习惯用法?
更新
我已经阅读了Stroustrup的常见问题 - 以及boost :: container文档。起初,我对Boost :: container感到非常鼓舞,但很遗憾没有看到如何在这些容器中使用有状态分配器的具体示例。我已经能够简化我原来的问题......给出一个结构:
struct DataUnit { map<int,string> m; list<int> l; }
我如何确保对于DataUnit的每个实例,都有一个池,m和l的内部成分是从哪个池中分配的?如果我将自定义分配器传递给map和list,则m和l将获得此容器的独立实例。我最初认为我可以使用get_allocator()来使用我的aerena / pool初始化分配器...但遗憾的是,在vector&lt; ...&gt;的默认构造函数之前调用allocate()。已经回来..所以我做不到。
更奇怪的是,我发现,已经涉及了boost :: container一段时间...... vanilla std容器有一个get_allocator()(在MSVC 2010和2012以及g ++ 4.6.3上),这表明标准这些上下文中的库对boost :: container具有类似的有状态分配器支持。
不幸的是,我仍然没有解决原始问题的可行解决方案(尽管我现在可以更有说服力地表达它。)
更新2
谢谢,pmr,你的最后评论是我给出的“正确答案” - 如果你把它归为一个答案。 :)我的问题,找到了boost :: container,是我原本期望它的文档显式有关任何新功能 - 比如在构造时传递allocator对象......而且我没有检查过boost :: container源代码代码正常。我现在确信,Boost :: container为我上面提到的所有问题提供了一个非常优雅和直观(如果记录不完整)的解决方案。再次感谢!
答案 0 :(得分:1)
警告:完全未经测试的代码。我不知道它是哪个“成语” - 但下面的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();
}
};
P.S。这个想法借鉴了微软的CAtlPlex类,这是大多数微软的模板容器(列表,地图,哈希图)通常比STL版本快2倍的首要原因。自从我放弃使用std :: vector,std :: set,std :: list等支持ATL的等价物后,我变得更快乐了。