#include <memory>

namespace std
{
    template <class T>
    class allocator
    {
        //...
    };
}

Object, dealing memory allocation and deletion.
Allocator represents a special memory model.
It is used as an abstraction to translate the need
to use memory into a raw call for memory.

Originally were introduced as part of STL to handle
the nasty problem of different pointer types on PCs.
(near, far, huge...).
Now they are serve as a base for technical solutions
that use different memory models, such as shared memory,
garbage collection and object-oriented databases.


Usage:

std::vector<int, MyAlloc<int> >     v;

std::map<int, float, less<int>,
            MyAlloc<std::pair<const int, float> > > m;

typedef std::basic_string<char, std::char_traits<char>,
            MyAlloc<char> > xstring;

typedef std::map<xstring, xstring, less<xstring>,
            MyAlloc<std::pair<const xstring, xstring> > > xmap;


if ( mymap.get_allocator() == s.get_allocator() )
{
    // ok. allocators of s and mymap are interchangeable:
    // one can free the others allocated memory
}
else
{
    // be care! objects are not interchangeable: don't mix 
    // elemenets with different allocators.
}




Fundamental allocator operations:


a.allocate(num)     Allocates memory for num elements

a.construct(p,val)  Initialize the element to which p refers with val1

a.destruct(p)       Destroys the element to which p refers

a.deallocate(p,num) Deallocates memory for num elemenst to which p refers



A (naive) vector implementation:


template <class T, class Allocator = allocator<T> >
class vector
{
public:
    explicit vector(const Allocator& = Allocator());
    explicit vector(size_type num, const T& val = T(),
                    const Allocator& = Allocator());
    template <class InputIterator>
    vector(InputIterator beg, InputIterator end,
                    const Allocator& = Allocator());
    vector(const vector<T,Allocator>& v);
    // ...    
private:
    Allocator   alloc;      // allocator
    T*          elems;      // array of elements
    size_type   numElems;   // number of elements
    size_type   sizeElems;  // size of memory for the elements
};

template <class T, class Allocator>
vector<T,Allocator>::vector(size_type num, const T& val,
                            const Allocator& a) : alloc(a)
{
    sizeElems = numElems = num;
    elems = alloc.allocate(num);

    for ( size_type i = 0; i < num; ++i )
    {
        // initialize element
        alloc.construct( &elems[i], val);
    }
}



Convenience functions for uninitialized memory:


uninitialized_fill( beg, end, val)      Initializes [beg,end[ with val

uninitialized_fill_n( beg, num, val)    Initialize num elements with val

uninitialized_copy( beg, end, mem)      Initialize elements starting at mem
                                        with elements of interval [beg,end[



This way the implementation is much easy:


template <class T, class Allocator>
vector<T,Allocator>::vector(size_type num, const T& val, const Allocator& a)
                       : alloc(a)
{
    sizeElems = numElems = num;
    elems = alloc.allocate(num);

    uninitialized_fill_n(elems, num, val);
}



A reserve():


template <class T, class Allocator>
void vector<T,Allocator>::reserve(size_type size)
{
    // reserve never shrinks memory for vector
    if ( size <= sizeElems )
        return;

    // allocate new memory
    T* newmem = alloc.allocate(size);

    uninitialized_copy(elems, elems+numElems, newmem);

    // destroy old elements
    for ( size_type i = 0; i < numElems; ++i )
    {
        alloc.destroy(&elems[i]);
    }

    // deallocate old memory
    alloc.deallocate(elems,sizeElems);

    sizeElems = size;
    elems     = newmem;
}



Raw storage iterators:


The class raw_storage_iterator is provided to iterate over
uninitialized memory to initialize it.


copy( x.begin(), x.end(),                   // source
        raw_storage_iterator<T*,T>(elems)); // destination
                                            // T* output iterator
                                            // T type of element        


Temporary buffer:

// not really exception - safe :-((
void f()
{
    // allocate memory for num elements of type T
    pair<T*, std::ptrdiff_t> p = get_temporary_buffer<T>(num);
    if ( p.second == 0 )
    {
        // could not allocate any memory
    }
    else if ( p.second < num )
    {
        // we have memory only for p.second elements
    }
    else
    {
        // we have enough memory
    }

    // ...

    if ( p.first != 0 )
    {
        // free allocated memory
        return_temporary_buffer(p.first);
    }
}



The default allocator:


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 T           value_type;

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

    // return address of values
    pointer       address(reference value) const;
    const_pointer address(const_reference value) const;

    // constructors and destructors
    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();

    // allocate but do not initialize num elements of type T
    // may throw std::bad_alloc exception
    pointer allocate(size_type num,
                allocator<void>::const_pointer hint = 0);

    // initialize elements
    void construct(pointer p, const T& value);

    // delete elements
    void destroy(pointer p);

    // deallocate storage 
    void deallocate(pointer p, size_type num);
};


The default allocator uses the global operators new and delete
to allocate and deallocate memory. Thus, allocate() may throw
bad_alloc exception. However the default allocator may be optimized
by reusing deallocated memory or by allocating more memory than
needed to save time for additional allocations. So the exact moments,
when operator new and delete are called is unspecified.


Struct rebind:

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

This template structure provides the ability that any allocator
may allocate storage of another type indirectly. If Allocator is
an allocatortype, then

Allocator::rebind<T2>::other

is the type of the same allocator specialized for elements of type T2.
Rebind is useful, if you implement a container and have to allocate memory
for a type that differs from the element's type.


template <class T, class Allocator = allocator<T> >
class deque
{
    //...
private:
    typedef typename Allocator::rebind<T*>::other PtrAllocator;

    Allocator       alloc;          // allocator for values of T
    PtrAllocator    block_alloc;    // allocator for values of T*
    T**             elems;          // array of blocks of elements
};




Default allocator for void:


template<>
class allocator<void>
{
public:
    typedef void*       pointer;
    typedef const void* const_pointer;
    typedef void        value_type;
    template <class U>
    struct rebind
    {
        typedef allocator<U> other;
    };
};



User defined allocator:


#include <limits>
#include <iostream>

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

       // rebind allocator to type U
       template <class U>
       struct rebind {
           typedef MyAlloc<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
        */
       MyAlloc() throw() { }
       MyAlloc(const MyAlloc&) throw() { }
       template <class U>
         MyAlloc (const MyAlloc<U>&) throw() { }
       ~MyAlloc() throw() { }

       // return maximum number of elements that can be allocated
       size_type max_size () const throw() {
           return std::numeric_limits<std::size_t>::max() / sizeof(T);
       }

       // allocate but don't initialize num elements of type T
       pointer allocate (size_type num, const void* = 0) {
           // print message and allocate memory with global new
           std::cerr << "allocate " << num << " element(s)"
                     << " of size " << sizeof(T) << std::endl;
           pointer ret = (pointer)(::operator new(num*sizeof(T)));
           std::cerr << " allocated at: " << (void*)ret << std::endl;
           return ret;
       }

       // 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) {
           // print message and deallocate memory with global delete
           std::cerr << "deallocate " << num << " element(s)"
                     << " of size " << sizeof(T)
                     << " at: " << (void*)p << std::endl;
           ::operator delete((void*)p);
       }
   };

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



#include <vector>
#include "myalloc.hpp"

int main()
{
    // create a vector, using MyAlloc<> as allocator
    std::vector<int,MyLib::MyAlloc<int> > v;

    // insert elements
    // - causes reallocations
    v.push_back(42);
    v.push_back(56);
    v.push_back(11);
    v.push_back(22);
    v.push_back(33);
    v.push_back(44);
}

allocate 1 element(s) of size 4
 allocated at: 0x804ad58
allocate 2 element(s) of size 4
 allocated at: 0x804ad68
deallocate 1 element(s) of size 4 at: 0x804ad58
allocate 4 element(s) of size 4
 allocated at: 0x804ad78
deallocate 2 element(s) of size 4 at: 0x804ad68
allocate 8 element(s) of size 4
 allocated at: 0x804ad90
deallocate 4 element(s) of size 4 at: 0x804ad78
deallocate 8 element(s) of size 4 at: 0x804ad90


====================================================


template <class ForwIter, class T>
void uninitialized_fill( ForwIter beg, ForwIter end, const T& value)
{
    typedef typename iterator_traits<ForwIter>::value_type VT;
    ForwIter save(beg);
    try
    {
        for( ; beg != end; ++beg )
        {
            new (static_cast<void*>(&*beg)) VT(value);
        }
    }
    catch(...)
    {
        for( ; save != beg ; ++save )
        {
            save->~VT();
        }
        throw;
    }
}

template <class ForwIter, class Size, class T>
void uninitialized_fill_n( ForwIter beg, Size num, const T& value)
{
    typedef typename iterator_traits<ForwIter>::value_type VT;
    ForwIter save(beg);
    try
    {
        for( ; num--; ++beg )
        {
            new (static_cast<void*>(&*beg)) VT(value);
        }
    }
    catch(...)
    {
        for( ; save != beg ; ++save )
        {
            save->~VT();
        }
        throw;
    }
}


template <class ForwIter, class Size, class T>
void uninitialized_copy( InputIter beg, InputIter end, ForwIter dest)
{
    typedef typename iterator_traits<ForwIter>::value_type VT;
    ForwIter save(beg);
    try
    {
        for( ; beg != end; ++beg, ++dest )
        {
            new (static_cast<void*>(&*dest)) VT(*beg);
        }
        return dest;
    }
    catch(...)
    {
        for( ; save != dest ; ++save )
        {
            save->~VT();
        }
        throw;
    }
}





Garbage collection:


Boehm-Demers-Weiser (BDW) conservative garbage collector

http://www.hpl.hp.com/personal/Hans_Boehm/gc/


The Boehm-Demers-Weiser conservative garbage collector can be used
as a garbage collecting replacement for C malloc or C++ new. It allows
you to allocate memory basically as you normally would, without explicitly
deallocating memory that is no longer useful. The collector automatically
recycles memory when it determines that it can no longer be otherwise
accessed.

The collector is also used by a number of programming language implementations
that either use C as intermediate code, want to facilitate easier
interoperation with C libraries, or just prefer the simple collector
interface.

Alternatively, the garbage collector may be used as a leak detector
for C or C++ programs, though that is not its primary goal.

Typically several versions will be available. Usually you should first
try to use gc_source/gc.tar.gz, which is normally an older, more stable
version.


Platforms

The collector is not completely portable, but the distribution includes
ports to most standard PC and UNIX/Linux platforms. The collector should
work on Linux, *BSD, recent Windows versions, MacOS X, HP/UX, Solaris,
Tru64, Irix and a few other operating systems. Some ports are more polished
than others.

Irix pthreads, Linux threads, Win32 threads, Solaris threads (old style and
pthreads), HP/UX 11 pthreads, Tru64 pthreads, and MacOS X threads are
supported in recent versions.


Scalable multiprocessor versions

Kenjiro Taura, Toshio Endo, and Akinori Yonezawa have made available
a parallel collector based on this one. Their collector takes advantage
of multiple processors during a collection. Starting with collector
version 6.0alpha1 we also do this, though with more modest processor
scalability goals.


Some Collector Details

The collector uses a mark-sweep algorithm. It provides incremental
and generational collection under operating systems which provide the
right kind of virtual memory support. (Currently this includes SunOS[45],
IRIX, OSF/1, Linux, and Windows, with varying restrictions.) It allows
finalization code to be invoked when an object is collected. It can take
advantage of type information to locate pointers if such information is
provided, but it is usually used without such information.


Performance of the nonincremental collector is typically competitive with
malloc/free implementations. Both space and time overhead are likely to be
only slightly higher for programs written for malloc/free.

For programs allocating primarily very small objects, the collector may be
faster; for programs allocating primarily large objects it will be slower.
If the collector is used in a multithreaded environment and configured for
thread-local allocation, it may in some cases significantly outperform
malloc/free allocation in time.

We also expect that in many cases any additional overhead will be more than
compensated for by decreased copying etc. if programs are written and tuned
for garbage collection.



The garbage collector uses a modified mark-sweep algorithm. Conceptually
it operates roughly in four phases, which are performed occasionally as
part of a memory allocation:

1. Preparation Each object has an associated mark bit. Clear all mark bits,
   indicating that all objects are potentially unreachable.

2. Mark phase Marks all objects that can be reachable via chains of pointers
   from variables. Often the collector has no real information about the
   location of pointer variables in the heap, so it views all static data
   areas, stacks and registers as potentially containing pointers. Any bit
   patterns that represent addresses inside heap objects managed by the
   collector are viewed as pointers. Unless the client program has made heap
   object layout information available to the collector, any heap objects
   found to be reachable from variables are again scanned similarly.

3. Sweep phase Scans the heap for inaccessible, and hence unmarked, objects,
   and returns them to an appropriate free list for reuse. This is not really
   a separate phase; even in non incremental mode this is operation is usually
   performed on demand during an allocation that discovers an empty free list.
   Thus the sweep phase is very unlikely to touch a page that would not have
   been touched shortly thereafter anyway.

4. Finalization phase Unreachable objects which had been registered for
   finalization are enqueued for finalization outside the collector.



Time overhead: +20-30 %

Memory overhead: BIIIG