15.4 A User-Defined Allocator

Writing your own allocator is not very hard. The most important issue is how you allocate or deallocate the storage. The rest is more or less obvious. As an example, let's look at a naive implementation of the default allocator:

//util/defalloc.hpp

namespace std {
template <class T>
class allocator {
public:
//type definitions
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef Tvalue_type;

//rebind allocator to type U
template <class U>
struct rebind {
typedef allocator<U> other;
};

//return address of values
pointer address (reference value) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
/*constructors and destructor
*-nothing to do because the allocator has no state
*/
allocator() throw() {
}
allocator(const allocator&) throw() {
}
template <class U>
allocator (const allocator<U>&) throw() {
}
~allocator() throw() {
}

//return maximum number of elements that can be allocated
size_type max_size () const throw() {
//for numeric_limits see Section 4.3, page 59
return numeric_limits<size_t>::max() / sizeof(T);
}

//allocate but don't initialize num elements of type T
pointer allocate (size_type num,
allocator<void>::const_pointer hint = 0) {
//allocate memory with global new
return (pointer) (::operator new(num*sizeof(T)));
}

//initialize elements of allocated storage p with value value
void construct (pointer p, const T& value) {
//initialize memory with placement new
new((void*)p)T(value);
}

//destroy elements of initialized storage p
void destroy (pointer p) {
// destroy objects by calling their destructor
p->~T();
}

//deallocate storage p of deleted elements
void deallocate (pointer p, size_type num) {
//deallocate memory with global delete
::operator delete((void*)p));
}
};

//return that all specializations of this allocator are interchangeable
template <class T1, class T2>
bool operator== (const allocator<T1>&,
const allocator<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!= (const allocator<T1>&,
const allocator<T2>&) throw() {
return false;
}
}

Using this base implementation you should find it no problem to implement your own allocator. Typically, the only things that differ from this implementation are max_size(), allocate(), and deallocate(). In these three functions, you program your own policy of memory allocation, such as reusing memory instead of freeing it immediately, using shared memory, or mapping the memory to a segment of an object-oriented database.