Back to Item 9: Avoid hiding the "normal" form of new.
Continue to Constructors, Destructors, and Assignment Operators

Item 10: Write `operator` `delete` if you write `operator` `new`.

Let's step back for a moment and return to fundamentals. Why would anybody want to write their own version of `operator` `new` or `operator` `delete` in the first place?

More often than not, the answer is efficiency. The default versions of `operator` `new` and `operator` `delete` are perfectly adequate for general-purpose use, but their flexibility inevitably leaves room for improvements in their performance in a more circumscribed context. This is especially true for applications that dynamically allocate a large number of small objects.

As an example, consider a class for representing airplanes, where the `Airplane` class contains only a pointer to the actual representation for airplane objects (a technique discussed in Item 34):

    class AirplaneRep { ... };      // representation for an
                                    // Airplane object
    class Airplane {
    public:
      ...
    private:
      AirplaneRep *rep;             // pointer to representation
    };

An `Airplane` object is not very big; it contains but a single pointer. (As explained in Items 14 and M24, it may implicitly contain a second pointer if the `Airplane` class declares virtual functions.) When you allocate an `Airplane` object by calling `operator` `new`, however, you probably get back more memory than is needed to store this pointer (or pair of pointers). The reason for this seemingly wayward behavior has to do with the need for `operator` `new` and `operator` `delete` to communicate with one another.

Because the default version of `operator` `new` is a general-purpose allocator, it must be prepared to allocate blocks of any size. Similarly, the default version of `operator` `delete` must be prepared to deallocate blocks of whatever size `operator` `new` allocated. For `operator` `delete` to know how much memory to deallocate, it must have some way of knowing how much memory `operator` `new` allocated in the first place. A common way for `operator` `new` to tell `operator` `delete` how much memory it allocated is by prepending to the memory it returns some additional data that specifies the size of the allocated block. That is, when you say this,

    Airplane *pa = new Airplane;

you don't necessarily get back a block of memory that looks like this:

Instead, you often get back a block of memory that looks more like this:

For small objects like those of class `Airplane`, this additional bookkeeping data can more than double the amount of memory needed for each dynamically allocated object (especially if the class contains no virtual functions).

If you're developing software for an environment in which memory is precious, you may not be able to afford this kind of spendthrift allocation. By writing your own `operator` `new` for the `Airplane` class, you can take advantage of the fact that all `Airplane` objects are the same size, so there isn't any need for bookkeeping information to be kept with each allocated block.

One way to implement your class-specific `operator` `new` is to ask the default `operator` `new` for big blocks of raw memory, each block of sufficient size to hold a large number of `Airplane` objects. The memory chunks for `Airplane` objects themselves will be taken from these big blocks. Currently unused chunks will be organized into a linked list — the free list — of chunks that are available for future `Airplane` use. This may make it sound like you'll have to pay for the overhead of a `next` field in every object (to support the list), but you won't: the space for the `rep` field (which is necessary only for memory chunks in use as `Airplane` objects) will also serve as the place to store the `next` pointer (because that pointer is needed only for chunks of memory not in use as `Airplane` objects). You'll arrange for this job-sharing in the usual fashion: you'll use a `union`.

To turn this design into reality, you have to modify the definition of `Airplane` to support custom memory management. You do it as follows:

    class Airplane {           // modified class — now supports
    public:                    // custom memory management

      static void * operator new(size_t size);

      ...

    private:
      union {
        AirplaneRep *rep;      // for objects in use
        Airplane *next;        // for objects on free list
      };

      // this class-specific constant (see Item 1) specifies how
      // many Airplane objects fit into a big memory block;
      // it's initialized below
      static const int BLOCK_SIZE;

      static Airplane *headOfFreeList;

    };

Here you've added the declarations for `operator` `new`, the union that allows the `rep` and `next` fields to occupy the same memory, a class-specific constant for specifying how big each allocated block should be, and a static pointer to keep track of the head of the free list. It's important to use a static member for this last task, because there's one free list for the entire class, not one free list for each `Airplane` object.

The next thing to do is to write the new `operator` `new`:

    void * Airplane::operator new(size_t size)
    {
      // send requests of the "wrong" size to ::operator new();
      // for details, see Item 8
      if (size != sizeof(Airplane))
        return ::operator new(size);

      Airplane *p =           // p is now a pointer to the
        headOfFreeList;       // head of the free list

      // if p is valid, just move the list head to the
      // next element in the free list
      if (p)
        headOfFreeList = p->next;

      else {
        // The free list is empty. Allocate a block of memory
        // big enough to hold BLOCK_SIZE Airplane objects
        Airplane *newBlock =
          static_cast<Airplane*>(::operator new(BLOCK_SIZE *
                                                sizeof(Airplane)));

        // form a new free list by linking the memory chunks
        // together; skip the zeroth element, because you'll
        // return that to the caller of operator new
        for (int i = 1; i < BLOCK_SIZE-1; ++i)
          newBlock[i].next = &newBlock[i+1];

        // terminate the linked list with a null pointer
        newBlock[BLOCK_SIZE-1].next = 0;

        // set p to front of list, headOfFreeList to
        // chunk immediately following
        p = newBlock;
        headOfFreeList = &newBlock[1];
      }

      return p;
    }

If you've read Item 8, you know that when `operator` `new` can't satisfy a request for memory, it's supposed to perform a series of ritualistic steps involving new-handler functions and exceptions. There is no sign of such steps above. That's because this `operator` `new` gets all the memory it manages from `::operator` `new`. That means this `operator` `new` can fail only if `::operator` `new` does. But if `::operator` `new` fails, it must engage in the new-handling ritual (possibly culminating in the throwing of an exception), so there is no need for `Airplane`'s `operator` `new` to do it, too. In other words, the new-handler behavior is there, you just don't see it, because it's hidden inside `::operator` `new`.

Given this `operator` `new`, the only thing left to do is provide the obligatory definitions of `Airplane`'s static data members:

    Airplane *Airplane::headOfFreeList;         // these definitions
                                                // go in an implemen-
    const int Airplane::BLOCK_SIZE = 512;       // tation file, not
                                                // a header file

There's no need to explicitly set `headOfFreeList` to the null pointer, because static members are initialized to 0 by default. The value for `BLOCK_SIZE`, of course, determines the size of each memory block we get from `::operator` `new`.

This version of `operator` `new` will work just fine. Not only will it use a lot less memory for `Airplane` objects than the default `operator` `new`, it's also likely to be faster, possibly as much as two orders of magnitude faster. That shouldn't be surprising. After all, the general version of `operator` `new` has to cope with memory requests of different sizes, has to worry about internal and external fragmentation, etc., whereas your version of `operator` `new` just manipulates a couple of pointers in a linked list. It's easy to be fast when you don't have to be flexible.

At long last we are in a position to discuss `operator` `delete`. Remember `operator` `delete`? This Item is about `operator` `delete`. As currently written, your `Airplane` class declares `operator` `new`, but it does not declare `operator` `delete`. Now consider what happens when a client writes the following, which is nothing if not eminently reasonable:

    Airplane *pa = new Airplane;        // calls
                                        // Airplane::operator new
    ...

    delete pa;                          // calls ::operator delete

If you listen closely when you read this code, you can hear the sound of an airplane crashing and burning, with much weeping and wailing by the programmers who knew it. The problem is that `operator` `new` (the one defined in `Airplane`) returns a pointer to memory without any header information, but `operator` `delete` (the default, global one) assumes that the memory it's passed does contain header information! Surely this is a recipe for disaster.

This example illustrates the general rule: `operator` `new` and `operator` `delete` must be written in concert so that they share the same assumptions. If you're going to roll your own memory allocation routine, be sure to roll one for deallocation, too. (For another reason why you should follow this advice, turn to the sidebar on placement `new` and placement `delete` in my article on counting objects in C++.)

Here's how you solve the problem with the `Airplane` class:

    class Airplane {        // same as before, except there's
    public:                 // now a decl. for operator delete
      ...

    static void operator delete(void *deadObject,
                                size_t size);

    };

    // operator delete is passed a memory chunk, which,
    // if it's the right size, is just added to the
    // front of the list of free chunks
    void Airplane::operator delete(void *deadObject,
                                   size_t size)
    {
      if (deadObject == 0) return;         // see Item 8

      if (size != sizeof(Airplane))     {  // see Item 8
        ::operator delete(deadObject);
        return;
      }

      Airplane *carcass =
        static_cast<Airplane*>(deadObject);

      carcass->next = headOfFreeList;
      headOfFreeList = carcass;
    }

Because you were careful in `operator` `new` to ensure that calls of the "wrong" size were forwarded to the global `operator` `new` (see Item 8), you must demonstrate equal care in ensuring that such "improperly sized" objects are handled by the global version of `operator` `delete`. If you did not, you'd run into precisely the problem you have been laboring so arduously to avoid — a semantic mismatch between `new` and `delete`.

Interestingly, the `size_t` value C++ passes to `operator` `delete` may be incorrect if the object being deleted was derived from a base class lacking a virtual destructor. This is reason enough for making sure your base classes have virtual destructors, but Item 14 describes a second, arguably better reason. For now, simply note that if you omit virtual destructors in base classes, `operator` `delete` functions may not work correctly.

All of which is well and good, but I can tell by the furrow in your brow that what you're really concerned about is the memory leak. With all the software development experience you bring to the table, there's no way you'd fail to notice that `Airplane`'s `operator` `new` calls `::operator` `new` to get big blocks of memory, but Airplane's `operator` `delete` fails to release those blocks.4 Memory leak! Memory leak! I can almost hear the alarm bells going off in your head.

Listen to me carefully: there is no memory leak.

A memory leak arises when memory is allocated, then all pointers to that memory are lost. Absent garbage collection or some other extralinguistic mechanism, such memory cannot be reclaimed. But this design has no memory leak, because it's never the case that all pointers to memory are lost. Each big block of memory is first broken down into `Airplane`-sized chunks, and these chunks are then placed on the free list. When clients call `Airplane::operator` `new`, chunks are removed from the free list, and clients receive pointers to them. When clients call `operator` `delete`, the chunks are put back on the free list. With this design, all memory chunks are either in use as `Airplane` objects (in which case it's the clients' responsibility to avoid leaking their memory) or are on the free list (in which case there's a pointer to the memory). There is no memory leak.

Nevertheless, the blocks of memory returned by `::operator` `new` are never released by `Airplane::operator` `delete`, and there has to be some name for that. There is. You've created a memory pool. Call it semantic gymnastics if you must, but there is an important difference between a memory leak and a memory pool. A memory leak may grow indefinitely, even if clients are well-behaved, but a memory pool never grows larger than the maximum amount of memory requested by its clients.

It would not be difficult to modify `Airplane`'s memory management routines so that the blocks of memory returned by `::operator` `new` were automatically released when they were no longer in use, but there are two reasons why you might not want to do it.

The first concerns your likely motivation for tackling custom memory management. There are many reasons why you might do it, but the most common one is that you've determined (see Item M16) that the default `operator` `new` and `operator` `delete` use too much memory or are too slow (or both). That being the case, every additional byte and every additional statement you devote to tracking and releasing those big memory blocks comes straight off the bottom line: your software runs slower and uses more memory than it would if you adopted the pool strategy. For libraries and applications in which performance is at a premium and you can expect pool sizes to be reasonably bounded, the pool approach may well be best.

The second reason has to do with pathological behavior. Suppose `Airplane`'s memory management routines are modified so `Airplane`'s `operator` `delete` releases any big block of memory that has no active objects in it. Now consider this program:

    int main()
    {
      Airplane *pa = new Airplane;     // first allocation: get big
                                       // block, make free list, etc.

      delete pa;                       // block is now empty;
                                       // release it

      pa = new Airplane;               // uh oh, get block again,
                                       // make free list, etc.

      delete pa;                       // okay, block is empty
                                       // again; release it

      ...                              // you get the idea...

      return 0;
    }

This nasty little program will run slower and use more memory than with even the default `operator` `new` and `operator` `delete`, much less the pool-based versions of those functions!

Of course, there are ways to deal with this pathology, but the more you code for uncommon special cases, the closer you get to reimplementing the default memory management functions, and then what have you gained? A memory pool is not the answer to all memory management questions, but it's a reasonable answer to many of them.

In fact, it's a reasonable answer often enough that you may be bothered by the need to reimplement it for different classes. "Surely," you think to yourself, "there should be a way to package the notion of a fixed-sized memory allocator so it's easily reused." There is, though this Item has droned on long enough that I'll leave the details in the form of the dreaded exercise for the reader.

Instead, I'll simply show a minimal interface (see Item 18) to a `Pool` class, where each object of type `Pool` is an allocator for objects of the size specified in the `Pool`'s constructor:

    class Pool {
    public:
      Pool(size_t n);                      // Create an allocator for
                                           // objects of size n

      void * alloc(size_t n)  ;            // Allocate enough memory
                                           // for one object; follow
                                           // operator new conventions
                                           // from Item 8

      void free(  void *p, size_t n);      // Return to the pool the
                                           // memory pointed to by p;
                                           // follow operator delete
                                           // conventions from Item 8

      ~Pool();                             // Deallocate all memory in
                                           // the pool
    };

This class allows `Pool` objects to be created, to perform allocation and deallocation operations, and to be destroyed. When a `Pool` object is destroyed, it releases all the memory it allocated. This means there is now a way to avoid the memory leak-like behavior that `Airplane`'s functions exhibited. However, this also means that if a `Pool`'s destructor is called too soon (before all the objects using its memory have been destroyed), some objects will find their memory yanked out from under them before they're done using it. To say that the resulting behavior is undefined is being generous.

Given this `Pool` class, even a Java programmer can add custom memory management capabilities to `Airplane` without breaking a sweat:

    class Airplane {
    public:

      ...                               // usual Airplane functions

      static void * operator new(size_t size);
      static void operator delete(void *p, size_t size);

    private:
      AirplaneRep *rep;                 // pointer to representation
      static Pool memPool;              // memory pool for Airplanes

    };

    inline void * Airplane::operator new(size_t size)
    { return memPool.alloc(size); }

    inline void Airplane::operator delete(void *p,
                                          size_t size)
    { memPool.free(p, size); }

    // create a new pool for Airplane objects; this goes in
    // the class implementation file
    Pool Airplane::memPool(sizeof(Airplane));

This is a much cleaner design than the one we saw earlier, because the `Airplane` class is no longer cluttered with non-airplane details. Gone are the `union`, the head of the free list, the constant defining how big each raw memory block should be, etc. That's all hidden inside `Pool`, which is really where it should be. Let `Pool`'s author worry about memory management minutiae. Your job is to make the `Airplane` class work properly.

Now, it's interesting to see how custom memory management routines can improve program performance, and it's worthwhile to see how such routines can be encapsulated inside a class like `Pool`, but let us not lose sight of the main point. That point is that `operator` `new` and `operator` `delete` need to work together, so if you write `operator` `new`, be sure to write `operator` `delete`, as well.

4 I write this with certainty, because I failed to address this issue in the first edition of this book, and many readers upbraided me for the omission. There's nothing quite like a few thousand proofreaders to demonstrate one's fallibility, sigh.
Return
Back to Item 9: Avoid hiding the "normal" form of new.
Continue to Constructors, Destructors, and Assignment Operators