Because pools are designed for speed, they do not check their arguments as exhaustively as they might. Moreover, they can introduce substantial space overhead if they are used in inappropriate contexts. Here are a few suggestions for ways to avoid trouble.
Free an element in the rightPool There is no efficient way to verify that an element is indeed part of the Pool from which it is being freed. Allocating an element in one Pool and freeing it in another is a sure way to cause mayhem, probably much later on when it has become virtually impossible to trace. Using pools in member functions is one way to help avoid this.
Use new and delete in pairs. A user-defined operator new is used only for allocating single objects. The system memory allocator is still used for arrays, because it is able to handle variable-length blocks. Thus, saying:
Thinglist* tp; tp = new Thing[N];
will not call Thinglist::operator new. However, a delete statement has no way of knowing how the memory being deleted was allocated. Thus, saying
delete tp; /* wrong */
will cause the first Thinglist to be linked into Thingpool and subsequent disaster. Instead, say:
which will call the system allocator. Note that the delete syntax is required anyway if Thinglist has a destructor; it is therefore good practice to use delete for every array that was allocated with new[N].
Every Pool allocates at least one block. Thus is probably unwise to define, say, a large array of pools. It is also dubious to define a class with a Pool member, as that would create a Pool for each object of that class.
There is no substitute for common sense. Pools will not solve all performance problems, and they are not free. They can dramatically speed up programs that use them carefully and drown programs that misuse them.