You can use malloc/free for the basic allocation. Handling full
standards compliance for new is a bit tricky; you need something like:
void*
operator new( size_t n )
{
void* results = malloc( n );
while ( results == NULL ) {
if ( std::get_new_handler() == NULL ) {
throw std::bad_alloc();
}
(*std::get_new_handler())();
results = malloc( n );
}
return results;
}
Often, however, you don't need such full compliance. If you say that
you don't support setting the new_handler, for example, you can
simplify greatly. In the overloaded version I use for testing, in fact,
if malloc really fails, I abort (but this version has options to
trigger a failure of new, on demand, since I want to test that my code
reacts correctly to it as well).
If you're logging, be very careful to avoid endless recursion. The only
functions guaranteed not to use operator new in the standard library
are malloc and free. Of course, a lot have no reason to allocate
dynamically, and I don't worry about functions like memcpy or
strlen. In practice, you are probably safe with any of the functions
in the C library (although in theory, printf could be implemented in
terms of iostream). But any use of iostream, locale or the standard
containers is out, unless you protect against recursion:
void*
operator new( size_t n )
{
static int recursionCount = 0;
++ recursionCount;
void* results = malloc() ;
// Any additional logic you need...
if ( recursionCount == 1 ) {
logAllocation( results, n );
}
-- recursionCount;
return results;
}
Formally, you should do the same for operator delete, although in
practice, if you're logging to a file, I wouldn't expect any delete
except in close() or the destructor.