Don't abbreviate unnecessarily
Most of the names you've chosen for variables and functions look fine to me, but there are some weird, unnecessary abbreviations:
resiz -> just write resize
alloc_sz -> I would write alloc_size or size
Some abbreviations are quite commonly used so I'd keep those (like Elems and alloc instead of Elements and allocation). However, be consistent: why is it nElems but m_MaxElements?
Create a class chunk
Your pool consists of multiple chunks, and each chunk is an array of nodes. It makes sense to make a proper type for chunks as well. While you are at it, avoid doing manual memory allocations, and let std::vector do it for you. The alloc_chunk() function can become the constructor of chunk.
struct chunk {
std::vector<node> nodes;
chunk(size_t nElems): nodes(nElems) {
for (auto &node: nodes)
node.next = &node + 1;
nodes.back().next = nullptr;
}
};
But this also leads me to:
Consider using a std::deque to store nodes
Your allocator requires each node, once allocated, to be at the same place in memory for the rest of its life. If you resize a std::vector or use realloc(), the storage might be moved. This is why you are using chunks. However, there is a STL container that gives you stable pointers and the ability to resize: std::deque. This allows you to get rid of chunks.
template <typename T>
class pool {
struct node {/* same as before */};
std::deque<node> m_Nodes;
node *m_Head{};
public:
pool(pool const&) = delete;
pool& operator=(pool const&) = delete;
pool() = default;
pool(pool&& o) = default;
...
T *alloc() {
if (!m_Head) {
m_Nodes.emplace_back({});
m_Head = &m_Nodes.back();
}
...
}
...
};
Performance
Your allocator is very good when storing large objects and when the game doesn't need to allocate large batches of objects at a time. However, it has the overhead of one pointer per element, so if you are storing small objects, you might be wasting a lot of memory. Furthermore, there is no better way to allocate a batch of objects than to allocate them one by one.
If you want to store small objects (for example, struct message is not that big), then instead of having a linked list of free nodes, it might be better to use (a tree of) bitmaps to store which elements of the chunk are used or not. Finding a region of consecutive free elements might also be much easier this way.