12.6 Using Custom Allocators
The STL and STL extension-based classes now accept an additional template allocator argument. The allocator argument has a default value of std::allocator<T> derived from the Standard C++ Library.
The use of custom allocators depends on the ANSI compliance of your compiler and your Standard C++ Library implementation. If allocators can be used, you can also provide your own allocator to customize memory management in an application.
In order to provide your own class template as an allocator, the template must conform to the particular interface described by the Standard C++ Library. This consists of member functions and typedefs as well as their syntactic and semantic requirements.
Here is a partial code snippet of a simple allocator:
 
template <class T>
class my_allocator
{
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 T value_type;
 
template <class U>
struct rebind { typedef allocator<U> other; };
// remaining member functions described below
// ...
};
The rebind member allows a container to construct an allocator for some arbitrary datatype. The allocator type is determined by the template parameter.
For example, a container may need to allocate types other than T (i.e. list nodes or hash buckets). In this case the container could obtain correct type, typically with a typedef:
 
typedef A::rebind<Node>::other Node_Allocator;
User-defined allocators must also be equality comparable; if a and b are instances of a user-defined allocator, then the expressions (a == b) and (a != b) must be well-formed.
12.6.1 Template Functions
Allocator class templates must meet all the above requirements. They must also provide these other member functions.
Constructors
my_allocator() throw();
my_allocator (const allocator&) throw ();
template <class U>
my_allocator(const my_allocator<U>&);
Destructor
~my_allocator();
Assignment operators
template <class U>
my_allocator& operator=(const my_allocator<U>&) throw();
Public Member Functions
pointer address(reference r) const;
Returns the address of r as a pointer type. This function and the following function are used to convert references to pointers.
const_pointer address(const_reference r) const;
Returns the address of r as a const_pointer type.
pointer allocate(size_type n,
allocator<U>::const_pointer hint=0);
Allocates storage for n values of T. Uses the value of hint to optimize storage placement, if possible.
void deallocate(pointer);
Deallocates storage obtained by a call to allocate.
size_type max_size();
Returns the largest possible storage available through a call to allocate.
void construct(pointer p, const_reference val);
Constructs an object of type T at the location of p, using the value of val in the call to the constructor for T.
void destroy(pointer p);
Calls the destructor on the value pointed to by p.
12.6.2 Example
The example below defines a simple custom allocator. This can be used to test your compiler or the Standard C++ Library's support for custom allocators. It checks if the allocator argument that is passed in the code is actually used:
 
#include <rw/tvdlist.h>
#include <rw/cstring.h>
#include <iostream>
 
template <class T>
class my_allocator
{
public:
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 T value_type;
 
my_allocator() {}
my_allocator(const my_allocator&) {}
 
 
 
pointer allocate(size_type n, const void * = 0) {
T* t = (T*) malloc(n * sizeof(T));
std::cout
<< " used my_allocator to allocate at address "
<< t << " (+)" << std::endl;
return t;
}
void deallocate(void* p, size_type) {
if (p) {
free(p);
std::cout
<< " used my_allocator to deallocate at address "
<< p << " (-)" <<
std::endl;
}
}
 
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
my_allocator<T>& operator=(const my_allocator&) { return *this; }
void construct(pointer p, const T& val)
{ new ((T*) p) T(val); }
void destroy(pointer p) { p->~T(); }
 
size_type max_size() const { return size_t(-1); }
 
template <class U>
struct rebind { typedef my_allocator<U> other; };
 
template <class U>
my_allocator(const my_allocator<U>&) {}
 
template <class U>
my_allocator& operator=(const my_allocator<U>&) { return *this; }
};
 
int main()
{
const int numItems = 100;
std::cout << "\nCreating a RWTValDlist with a default allocator"
<< std::endl;
 
RWTValDlist<RWCString> regular;
 
 
std::cout << "\nInserting " << numItems
<< " items" << std::endl;
 
for (int i = 0; i < numItems; ++i) {
regular.insert(RWCString('a' + i, i));
}
 
 
std::cout << "\n\nCreating a RWTValDlist with my_allocator type"
<< std::endl;
 
RWTValDlist<RWCString, my_allocator<RWCString> > custom;
 
std::cout << "\nInserting " << numItems
<< " items\n" << std::endl;
 
for (int i = 0; i < numItems; ++i) {
custom.insert(RWCString('a' + i, i));
}
return 0;
}
Program Output
Creating a RWTValDlist with a default allocator
 
Inserting 100 items
 
Creating a RWTValDlist with my_allocator type
used my_allocator to allocate at address 0080ABD0 (+)
used my_allocator to allocate at address 0080AC08 (+)
 
Inserting 100 items
 
used my_allocator to allocate at address 0080AC40 (+)
used my_allocator to allocate at address 0080AC78 (+)
used my_allocator to allocate at address 0080C6F0 (+)
used my_allocator to allocate at address 0080C728 (+)
used my_allocator to allocate at address 0080FB28 (+)
used my_allocator to allocate at address 00820068 (+)
used my_allocator to deallocate at address 00820068 (-)
used my_allocator to deallocate at address 0080FB28 (-)
used my_allocator to deallocate at address 0080C728 (-)
used my_allocator to deallocate at address 0080C6F0 (-)
used my_allocator to deallocate at address 0080AC78 (-)
used my_allocator to deallocate at address 0080AC40 (-)
used my_allocator to deallocate at address 0080AC08 (-)
used my_allocator to deallocate at address 0080ABD0 (-)
In the output listed above, it can be seen that, when no custom allocator was used, the RWTValDlist was created and 100 items were inserted. However, when the list was instantiated with my_allocator, instances of this class were used in 8 allocations and deallocations of heap memory. This proves that the example used my_allocator for memory management.