Memory Poolのいくつかの考え方と実現について


『Effective C++』(Second Edition)ではItem 10の「Write operator delete if you write operator new」でMemory Poolテクノロジーについて言及しています.void*Airplane::operator new(size_t size){if(size!=sizeof(Airplane))return::operator new(size);Airplane*p=headOfFreeList;if(p)headOfFreeList=p>>next;else{Airplane*newBlock=static_cast(::operator new(BLOCK_SIZE*sizeof(Airplane*Airplane*Airplane*Airplane*Airplane***>(::operator new(BLOCK_SIZE*sizeof(Airplane*Airplane))));for(int i=1; iしかし、明らかに、例で示した操作はAirplaneクラスに対してしか操作できないので、どのクラスに対してもこのようなMemory Pool操作を行うことができるのではないかと考えてみましょう.
最初のアイデアはtemplate.昼にCMemoryPoolテンプレートクラスを書きました.以下に示す
#ifndef __MEMORYPOOL_H__
#define __MEMORYPOOL_H__
#include <list>

template <class _T>
class CMemoryPool  
{
public:
CMemoryPool(size_t init_size = 100) : INIT_SIZE(init_size), m_memoryHeadList(5)
{
headOfFreeList = NULL;
}
~CMemoryPool()
{
std::list<Envelope *>::iterator iter;
for(iter = m_memoryHeadList.begin(); iter != m_memoryHeadList.end(); ++iter)
{
Envelope *block = *iter;
::operator delete(block);
}
}
_T *alloc()
{
Envelope *p = headOfFreeList;
if(p)
headOfFreeList = p->next;
else
{
const size_t ObjectSize = (sizeof(_T) > sizeof(Envelope)) ? sizeof(_T) : sizeof(Envelope);
//  
Envelope *newBlock = static_cast<Envelope *>(::operator new(INIT_SIZE * ObjectSize));
//  
for(int i=1; i<INIT_SIZE; ++i)
((Envelope *)((char *)newBlock + i * ObjectSize))->next = (Envelope *)((char *)newBlock + (i+1) * ObjectSize);
((Envelope*)((char *)newBlock + (INIT_SIZE - 1) * ObjectSize))->next = NULL;
//  
m_memoryHeadList.push_back(newBlock);
p = newBlock;
headOfFreeList = (Envelope*)((char *)newBlock + ObjectSize);
}
return (_T *)p;
}
void free(_T *p)
{
if(p==NULL)
return;
Envelope *freeBlock = (Envelope *)p;
freeBlock->next = headOfFreeList;
headOfFreeList = freeBlock;
}
private:
union Envelope
{
_T *object;
union Envelope *next;
};
const size_t INIT_SIZE;//  
Envelope *headOfFreeList; //  
std::list<Envelope *> m_memoryHeadList; //  
};
#endif // !defined __MEMORYPOOL_H__

このテンプレートクラスは、組み込みタイプに対して、以下のように使用することができる.
#include <iostream>
#include "MemoryPool.h"
int main()
{
CMemoryPool<double> d_Pool;
double *p[10];
int i;
for(i=0; i<10; ++i)
{
p[i] = d_Pool.alloc();
std::cout<<p[i]<<std::endl;
}
for(i=0; i<5; ++i)
d_Pool.free(p[i]);
for(i=0; i<5; ++i)
p[i] = d_Pool.alloc();
for(i=0; i<10; ++i)
std::cout<<p[i]<<std::endl;
return 0;
}

このように使用するだけでは、所望の目的を達成することができない.重要なのは、独自のカスタムクラスについて、operator newとoperator deleteを再ロードしてMemory Poolを使用することです.
#include <iostream>
#include "MemoryPool.h"
class MyClass
{
public:
MyClass();
~MyClass();
static void * operator new(size_t t);
static void operator delete(void *p);
private:
static CMemoryPool<MyClass> st_objectPool;
};
CMemoryPool<MyClass> MyClass::st_objectPool(10);
MyClass::MyClass()
{
std::cout<<"A() called"<<std::endl;
}
MyClass::~MyClass()
{
std::cout<<"~A() called"<<std::endl;
}
void * MyClass::operator new(size_t t)
{
if(t != sizeof(MyClass))
return ::operator new(t);
return MyClass::st_objectPool.alloc();
}
void MyClass::operator delete(void *p)
{
MyClass::st_objectPool.free((MyClass *)p);
}

int main()
{
MyClass *ptr;
ptr = new MyClass;
delete ptr;
return 0;
}

これで、どのクラスに対してもCMemoryPoolクラスの書き方が完了しました.ベースクラスCMemoryPoolBaseClassをどのように書き、そのクラスを継承するすべてのサブクラスにこのようなメモリプールの動的メモリ割り当て方式を持たせるか、自然に考えられます.