14 April 2007

Placement new is your friend

It doesn't happen often in game programming when you want to be specific about how (or where) an object is constructed, but it does come up. Consider a simple block allocator:

struct FreeBlock {
FreeBlock* pNext;
FreeBlock( FreeBlock* pNextFree = 0 ) :
pNext( pNextFree ) {}
};

T* BlockAlloc<T>::alloc() {
if ( m_freeList ) {
FreeBlock* pBlock = m_freeList;
m_freeList = pBlock->pNext;
pBlock->~FreeBlock(); // Destruct FreeBlock
// Use placement new to construct T
return new ( pBlock ) T();
} else {
// Get raw memory and set up free list
...
}
}

void BlockAlloc<T>::free( T* pt ) {
if ( pt ) {
pt->~T(); // Destruct T
m_freeList = new ( pt ) FreeBlock( m_freeList );
}
}


This is a very simple example, used only for illustration. The use of placement new allows us to construct only the T objects that are currently in use, but allows us to allocate enough memory for a large number of them.

Calling the FreeBlock destructor is seemingly unnecessary since FreeBlock only has an implicit destructor. However, it's maintainable to do so. What if someone later adds a destructor to FreeBlock to, say, decrement a count of FreeBlock objects?

This is a simple example; placement new is great for many things: containers, singleton objects that you don't want on the heap (but want to control when they are constructed), memory management structures, game resource management and so on.

No comments: