Overloading global new & delete
This is the drastic approach, when the
global versions of new and delete are unsatisfactory for the whole
system. If you overload the global versions, you make the defaults completely
inaccessible – you can’t even call them from inside your
redefinitions.
The overloaded new must take an
argument of size_t (the Standard C standard type
for sizes). This argument is generated and passed to you by the compiler and is
the size of the object you’re responsible for allocating. You must return
a pointer either to an object of that size (or bigger, if you have some reason
to do so), or to zero if you can’t find the memory (in which case the
constructor is not called!). However, if you can’t find the memory,
you should probably do something more informative than just returning zero, like
calling the new-handler or throwing an exception, to signal that there’s a
problem.
The return value of operator new(
) is a void*, not a pointer to any particular type. All
you’ve done is produce memory, not a finished object – that
doesn’t happen until the constructor is called, an act the compiler
guarantees and which is out of your control.
The operator delete( )
takes a void* to memory that was allocated by operator new.
It’s a void* because operator delete only gets the pointer
after the destructor is called, which removes the object-ness from the
piece of storage. The return type is void.
Here’s a simple example showing how
to overload the global new and delete:
//: C13:GlobalOperatorNew.cpp
// Overload global new/delete
#include <cstdio>
#include <cstdlib>
using namespace std;
void* operator new(size_t sz) {
printf("operator new: %d Bytes\n", sz);
void* m = malloc(sz);
if(!m) puts("out of memory");
return m;
}
void operator delete(void* m) {
puts("operator delete");
free(m);
}
class S {
int i[100];
public:
S() { puts("S::S()"); }
~S() { puts("S::~S()"); }
};
int main() {
puts("creating & destroying an int");
int* p = new int(47);
delete p;
puts("creating & destroying an s");
S* s = new S;
delete s;
puts("creating & destroying S[3]");
S* sa = new S[3];
delete []sa;
} ///:~
Here you can see the general form for
overloading new and delete. These use the Standard C library
functions malloc( ) and
free( ) for the allocators (which is
probably what the default new and delete use as well!). However,
they also print messages about what they are doing. Notice that
printf( ) and
puts( ) are used rather than
iostreams. This is because when an iostream
object is created (like the global cin,
cout, and cerr), it calls new to allocate memory. With
printf( ), you don’t get into a deadlock because it
doesn’t call new to initialize itself.
In main( ), objects of
built-in types are created to prove that the overloaded new and
delete are also called in that case. Then a single object of type
S is created, followed by an array of S. For the array,
you’ll see from the number of bytes requested that extra memory is
allocated to store information (inside the array) about the number of objects it
holds. In all cases, the global overloaded versions of new and
delete are
used.