I found this article:
http://www.drdobbs.com/the-standard-librarian-what-are-allocato/184403759
Although it predates C++11, I can't find anything more recent, so here I
am playing with the code and trying to understand it.
I get the following output from my code (listed below):
Allocated 16 bytes for a struct std::_Container_proxy
Complex Number constructed
Allocated 24 bytes for a class ComplexNumber
Complex Number copy constructed
Complex Number deconstructed
Complex Number deconstructed
Deallocated 24 bytes for a class ComplexNumber
Deallocated 16 bytes for a struct std::_Container_proxy
Why are 16 bytes allocated before space for the actual data I am putting
into the std::vector and what is a container_proxy?
When 'placement new' is called, is that when the object is being copy
constructed? and if I understand right, copy constructed into the memory
I allocated?
What is the second argument in pointer allocate(size_type n,
const_pointer = 0) ?
What is the second argument in void deallocate(pointer p, size_type) ?
This makes no sense to me. What is the max size supposed to be and why
is it a negative number? Or is this some unsigned/signed trickery?
size_type max_size() const
{
return static_cast<size_type>(-1) / sizeof(value_type);
}
Here is my listing:
// Standard Includes
#include <iostream>
//------------------------------------------------------------------------------
/// <summary>
/// A STL compliant custom allocator
/// After C++11 this changes a bit by dropping much boiler plate code
///
/// Based on Dr Dobbs article
///
http://www.drdobbs.com/the-standard-librarian-what-are-allocato/184403759
/// </summary>
template <class T>
class malloc_allocator
{
public:
typedef T value_type;
typedef value_type * pointer;
typedef const value_type * const_pointer;
typedef value_type & reference;
typedef const value_type & const_reference;
typedef std::size_t size_type;
// typedef std::ptrdiff_t difference_type; // Gives compile error
malloc_allocator()
{
// Allocators before C++11 are expected to be stateless
}
malloc_allocator(const malloc_allocator & rhs)
{
}
~malloc_allocator()
{
}
void operator =(const malloc_allocator & rhs) = delete;
bool operator == (const malloc_allocator<T> & rhs)
{
return true;
}
bool operator != (const malloc_allocator<T> & rhs)
{
return false;
}
template <class U>
malloc_allocator(const malloc_allocator<U> &)
{
}
template <class U>
struct rebind
{
typedef malloc_allocator<U> other;
};
pointer address(reference x) const
{
return &x;
}
const_pointer address(const_reference x) const
{
return &x;
}
pointer allocate(size_type n, const_pointer = 0)
{
const size_type numBytes = n * sizeof(T);
void * p = std::malloc(numBytes);
if (!p)
{
throw std::bad_alloc();
}
std::cout << "Allocated " << numBytes << " bytes for a " <<
typeid(T).name() << std::endl;
return static_cast<pointer>(p);
}
void deallocate(pointer p, size_type)
{
const size_type numBytes = sizeof(*p);
std::free(p);
std::cout << "Deallocated " << numBytes << " bytes for a " <<
typeid(T).name() << std::endl;
}
size_type max_size() const
{
return static_cast<size_type>(-1) / sizeof(value_type);
}
void construct(pointer p, const value_type & x)
{
// 'Placement new' constructs an object at a specified location
// The custom allocator seperates allocation from construction
new(p) value_type(x);
}
void destroy(pointer p)
{
p->~value_type();
}
};
// Template specialization for void type
// Our custom allocator does not work when a container refers to void
pointers.
// We refer to sizeof(T) and T& which is not legal when T is void.
// Therefore we must specialize.
// Leaves out everything except what is needed for referring to void
pointers
template<> class malloc_allocator<void>
{
typedef void value_type;
typedef void * pointer;
typedef const void * const_pointer;
template <class U>
struct rebind
{
typedef malloc_allocator<U> other;
};
};
//------------------------------------------------------------------------------
class ComplexNumber
{
public:
ComplexNumber(double realPart, double complexPart);
ComplexNumber(const ComplexNumber & rhs);
virtual ~ComplexNumber();
protected:
double m_realPart;
double m_complexPart;
};
//------------------------------------------------------------------------------
ComplexNumber::ComplexNumber(double realPart, double complexPart)
:
m_realPart(realPart)
, m_complexPart(complexPart)
{
std::cout << "Complex Number constructed" << std::endl;
}
//------------------------------------------------------------------------------
ComplexNumber::ComplexNumber(const ComplexNumber & rhs)
:
m_realPart(rhs.m_realPart)
, m_complexPart(rhs.m_complexPart)
{
std::cout << "Complex Number copy constructed" << std::endl;
}
//------------------------------------------------------------------------------
ComplexNumber::~ComplexNumber()
{
std::cout << "Complex Number deconstructed" << std::endl;
}
//------------------------------------------------------------------------------
void RunCustomAllocator()
{
std::vector<ComplexNumber, malloc_allocator<ComplexNumber> > myVector;
ComplexNumber number(1, 1);
myVector.push_back(number);
}
//------------------------------------------------------------------------------
int main(int argc, char * argv[])
{
// RunNoMemoryManagement();
// RunSimpleMemoryManagement();
RunCustomAllocator();
return 0;
}