Overloading new & delete for a class
Although you don’t have to
explicitly say static, when you overload new and delete for
a class, you’re creating static member functions. As before, the
syntax is the same as overloading any other operator. When the compiler sees you
use new to create an object of your class, it chooses the member
operator new( ) over the global version. However, the global
versions of new and delete are used for all other types of objects
(unless they have their own new and delete).
In the following example, a primitive
storage allocation system
is
created for the class Framis. A chunk of memory is set aside in the
static data area at program start-up, and that memory is used to allocate space
for objects of type Framis. To determine which blocks have been
allocated, a simple array of bytes is used, one byte for each
block:
//: C13:Framis.cpp
// Local overloaded new & delete
#include <cstddef> // Size_t
#include <fstream>
#include <iostream>
#include <new>
using namespace std;
ofstream out("Framis.out");
class Framis {
enum { sz = 10 };
char c[sz]; // To take up space, not used
static unsigned char pool[];
static bool alloc_map[];
public:
enum { psize = 100 }; // frami allowed
Framis() { out << "Framis()\n"; }
~Framis() { out << "~Framis() ... "; }
void* operator new(size_t) throw(bad_alloc);
void operator delete(void*);
};
unsigned char Framis::pool[psize * sizeof(Framis)];
bool Framis::alloc_map[psize] = {false};
// Size is ignored -- assume a Framis object
void*
Framis::operator new(size_t) throw(bad_alloc) {
for(int i = 0; i < psize; i++)
if(!alloc_map[i]) {
out << "using block " << i << " ... ";
alloc_map[i] = true; // Mark it used
return pool + (i * sizeof(Framis));
}
out << "out of memory" << endl;
throw bad_alloc();
}
void Framis::operator delete(void* m) {
if(!m) return; // Check for null pointer
// Assume it was created in the pool
// Calculate which block number it is:
unsigned long block = (unsigned long)m
- (unsigned long)pool;
block /= sizeof(Framis);
out << "freeing block " << block << endl;
// Mark it free:
alloc_map[block] = false;
}
int main() {
Framis* f[Framis::psize];
try {
for(int i = 0; i < Framis::psize; i++)
f[i] = new Framis;
new Framis; // Out of memory
} catch(bad_alloc) {
cerr << "Out of memory!" << endl;
}
delete f[10];
f[10] = 0;
// Use released memory:
Framis* x = new Framis;
delete x;
for(int j = 0; j < Framis::psize; j++)
delete f[j]; // Delete f[10] OK
} ///:~
The pool of memory for the Framis
heap is created by allocating an array of bytes large enough to hold
psize Framis objects. The allocation map is psize elements
long, so there’s one bool for every block. All the values in the
allocation map are initialized to false using the aggregate
initialization trick of setting the first element so the compiler automatically
initializes all the rest to their normal default value (which is false,
in the case of bool).
The local operator new( )
has the same syntax as the global one. All it does is search through the
allocation map looking for a false value, then sets that location to
true to indicate it’s been allocated and returns the address of the
corresponding memory block. If it can’t find any memory, it issues a
message to the trace file and throws a bad_alloc
exception.
This is the first example of
exceptions that you’ve seen in this book. Since
detailed discussion of exceptions is delayed until Volume 2, this is a very
simple use of them. In operator new( ) there are two artifacts of
exception handling. First, the function argument list is followed by
throw(bad_alloc), which
tells the compiler and the reader that this function may throw an exception of
type bad_alloc. Second, if there’s no more
memory the function actually does throw the exception in the statement throw
bad_alloc. When an exception is thrown, the function stops executing and
control is passed to an exception handler, which is expressed as a
catch clause.
In main( ), you see the other
part of the picture, which is the try-catch clause. The
try block is surrounded
by braces and contains all the code that may throw exceptions – in this
case, any call to new that involves Framis objects. Immediately
following the try block is one or more
catch clauses, each one
specifying the type of exception that they catch. In this case,
catch(bad_alloc) says that that bad_alloc exceptions will be
caught here. This particular catch clause is only executed when a
bad_alloc exception is thrown, and execution continues after the end of
the last catch clause in the group (there’s only one here, but
there could be more).
In this example, it’s OK to use
iostreams because the global operator new(
) and delete( ) are untouched.
The operator delete( )
assumes the Framis address was created in the pool. This is a fair
assumption, because the local operator new( ) will be called
whenever you create a single Framis object on the heap – but not an
array of them: global new is used for arrays. So the user might
accidentally have called operator delete( ) without using the
empty bracket syntax to indicate array destruction. This would cause a problem.
Also, the user might be deleting a pointer to an object created on the stack. If
you think these things could occur, you might want to add a line to make sure
the address is within the pool and on a correct boundary (you may also begin to
see the potential of overloaded new and
delete for finding memory leaks).
operator delete( )
calculates the block in the pool that this pointer represents, and then sets the
allocation map’s flag for that block to false to indicate the block has
been released.
In main( ), enough
Framis objects are dynamically allocated to run out of memory; this
checks the out-of-memory behavior. Then one of the objects is freed, and another
one is created to show that the released memory is reused.
Because this allocation scheme is
specific to Framis objects, it’s probably much faster than the
general-purpose memory allocation scheme used for the default new and
delete. However, you should note that it doesn’t automatically work
if inheritance is used (inheritance is covered in Chapter
14).