C++ FAQ on destructor

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14

[11.10] What is “placement new” and why would I use it?

There are many uses of placement new. The simplest use is to place an object at a particular location in memory. This is done by supplying the place as a pointer parameter to the new part of a new expression:

 #include <new>        // Must #include this to use “placement new
#include "Fred.h"     
// Declaration of class Fredvoid someCode()
{
char memory[sizeof(Fred)];// Line #1
void* place = memory;          
// Line #2Fred* f = new(place) Fred();// Line #3 (see “DANGER” below)
// The pointers f and place will be equal
}

Line #1 creates an array of sizeof(Fred) bytes of memory, which is big enough to hold a Fred object. Line #2 creates a pointer place that points to the first byte of this memory (experienced C programmers will note that this step was unnecessary; it’s there only to make the code more obvious). Line #3 essentially just calls the constructor Fred::Fred(). The this pointer in the Fred constructor will be equal to place. The returned pointer f will therefore be equal to place.

ADVICE: Don’t use this “placement new” syntax unless you have to. Use it only when you really care that an object is placed at a particular location in memory. For example, when your hardware has a memory-mapped I/O timer device, and you want to place a Clock object at that memory location.

DANGER: You are taking sole responsibility that the pointer you pass to the “placement new” operator points to a region of memory that is big enough and is properly aligned for the object type that you’re creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn’t properly aligned, you can have a serious disaster on your hands (if you don’t know what “alignment” means, please don’t use the placement new syntax). You have been warned.

You are also solely responsible for destructing the placed object. This is done by explicitly calling the destructor:

 void someCode()
{
char memory[sizeof(Fred)];
void* p = memory;
Fred* f = new(p) Fred();

f->~Fred();   
// Explicitly call the destructor for the placed object
}

This is about the only time you ever explicitly call a destructor.

Note: there is a much cleaner but more sophisticated way of handling the destruction / deletion situation.

[11.9] But can I explicitly call a destructor if I’ve allocated my object with new?

Probably not.

Unless you used placement new, you should simply delete the object rather than explicitly calling the destructor. For example, suppose you allocated the object via a typical new expression:

 Fred* p = new Fred();

Then the destructor Fred::~Fred() will automagically get called when you delete it via:

 delete p;  // Automagically calls p->~Fred()

You should not explicitly call the destructor, since doing so won’t release the memory that was allocated for the Fred object itself. Remember: delete p does two things: it calls the destructor and it deallocates the memory.

[11.14] Is there a way to force new to allocate memory from a specific memory area?

Starting with a simple memory allocator function, allocate(), you would use placement new to construct an object in that memory. In other words, the following is morally equivalent to new Foo():

 void* raw = allocate(sizeof(Foo));  // line 1
Foo* p = new(raw) Foo();            
// line 2
 The next step is to turn your memory allocator into an object.

This kind of object is called a “memory pool” or a “memory arena.” This lets your users have more than one “pool” or “arena” from which memory will be allocated. Each of these memory pool objects will allocate a big chunk of memory using some specific system call (e.g., shared memory, persistent memory, stack memory, etc.; see below), and will dole it out in little chunks as needed. Your memory-pool class might look something like this:

 class Pool {
public:
void* alloc(size_t nbytes);
void dealloc(void* p);
private:
…data members used in your pool object…
};
void* Pool::alloc(size_t nbytes)
{

…your algorithm goes here…
}

void Pool::dealloc(void* p)
{

…your algorithm goes here…
}

Now one of your users might have a Pool called pool, from which they could allocate objects like this:

 Pool pool;

void* raw = pool.alloc(sizeof(Foo));
Foo* p = new(raw) Foo();

Or simply:

 Foo* p = new(pool.alloc(sizeof(Foo))) Foo();

The reason it’s good to turn Pool into a class is because it lets users create N different pools of memory rather than having one massive pool shared by all users. That allows users to do lots of funky things. For example, if they have a chunk of the system that allocates memory like crazy then goes away, they could allocate all their memory from a Pool, then not even bother doing any deletes on the little pieces: just deallocate the entire pool at once. Or they could set up a “shared memory” area (where the operating system specifically provides memory that is shared between multiple processes) and have the pool dole out chunks of shared memory rather than process-local memory. Another angle: many systems support a non-standard function often called alloca() which allocates a block of memory from the stack rather than the heap. Naturally this block of memory automatically goes away when the function returns, eliminating the need for explicit deletes. Someone could use alloca() to give the Pool its big chunk of memory, then all the little pieces allocated from that Pool act like they’re local: they automatically vanish when the function returns. Of course the destructors don’t get called in some of these cases, and if the destructors do something nontrivial you won’t be able to use these techniques, but in cases where the destructor merely deallocates memory, these sorts of techniques can be useful.

Okay, assuming you survived the 6 or 8 lines of code needed to wrap your allocate function as a method of a Pool class, the next step is to change the syntax for allocating objects. The goal is to change from the rather clunky syntax new(pool.alloc(sizeof(Foo))) Foo() to the simpler syntax new(pool) Foo(). To make this happen, you need to add the following two lines of code just below the definition of your Pool class:

 inline void* operator new(size_t nbytes, Pool& pool)
{
return pool.alloc(nbytes);
}

Now when the compiler sees new(pool) Foo(), it calls the above operator new and passes sizeof(Foo) and pool as parameters, and the only function that ends up using the funky pool.alloc(nbytes) method is your own operator new.

Now to the issue of how to destruct/deallocate the Foo objects. Recall that the brute force approach sometimes used with placement new is to explicitly call the destructor then explicitly deallocate the memory:

 void sample(Pool& pool)
{
Foo* p = new(pool) Foo();

p->~Foo();        
// explicitly call dtor
pool.dealloc(p);  
// explicitly release the memory
}

This has several problems, all of which are fixable:

  1. The memory will leak if Foo::Foo() throws an exception.
  2. The destruction/deallocation syntax is different from what most programmers are used to, so they’ll probably screw it up.
  3. Users must somehow remember which pool goes with which object. Since the code that allocates is often in a different function from the code that deallocates, programmers will have to pass around two pointers (a Foo* and a Pool*), which gets ugly fast (example, what if they had an array of Foos each of which potentially came from a different Pool; ugh).

We will fix them in the above order.

Problem 1 :

When you use the “normal” new operator, e.g., Foo* p = new Foo(), the compiler generates some special code to handle the case when the constructor throws an exception. The actual code generated by the compiler is functionally similar to this:

 // This is functionally what happens with Foo* p = new Foo()Foo* p;// don’t catch exceptions thrown by the allocator itself
void* raw = operator new(sizeof(Foo));

// catch any exceptions thrown by the ctor
try {
p = new(raw) Foo();  
// call the ctor with raw as this
}
catch (...) {
// oops, ctor threw an exception
operator delete(raw);
throw;  
// rethrow the ctor’s exception

The point is that the compiler deallocates the memory if the ctor throws an exception. But in the case of the “new with parameter” syntax (commonly called “placement new“), the compiler won’t know what to do if the exception occurs so by default it does nothing.

 // This is functionally what happens with Foo* p = new(pool) Foo():

void* raw = operator new(sizeof(Foo), pool);
// the above function simply returns “pool.alloc(sizeof(Foo))”

Foo* p = new(raw) Foo();
// if the above line “throws”, pool.dealloc(raw) is NOT called

So the goal is to force the compiler to do something similar to what it does with the global new operator. Fortunately it’s simple: when the compiler seesnew(pool) Foo(), it looks for a corresponding operator delete. If it finds one, it does the equivalent of wrapping the ctor call in a try block as shown above. So we would simply provide an operator delete with the following signature (be careful to get this right; if the second parameter has a different type from the second parameter of the operator new(size_t, Pool&), the compiler doesn’t complain; it simply bypasses the try block when your users saynew(pool) Foo()):

 void operator delete(void* p, Pool& pool)
{
pool.dealloc(p);
}

After this, the compiler will automatically wrap the ctor calls of your new expressions in a try block:

Problems #2 (“ugly therefore error prone”) and #3 (“users must manually associate pool-pointers with the object that allocated them, which is error prone”)are solved simultaneously with an additional 10-20 lines of code in one place. In other words, we add 10-20 lines of code in one place (your Pool header file) and simplify an arbitrarily large number of other places (every piece of code that uses your Pool class).

The idea is to implicitly associate a Pool* with every allocation.

Two methods are discussed in this FAQ, one is to use std::map<void *, pool * >. In other words, build a look-up table where the keys are allocation pointer and whose values are the associated pool * . it is essential that you insert a key/value pair into the map only in operator new(size_t,Pool&). In particular, you must not insert a key/value pair from the global operator new (e.g., you must not say, poolMap[p] = NULL in the global operator new). Reason: doing that would create a nasty chicken-and-egg problem — since std::map probably uses the global operator new, it ends up inserting a new entry every time inserts a new entry, leading to infinite recursion — bang you’re dead.

Another approach that is faster but might use more memory and is a little trickier is to prepend a Pool* just before all allocations. For example, if nbytes was 24, meaning the caller was asking to allocate 24 bytes, we would allocate 28 (or 32 if you think the machine requires 8-byte alignment for things like doubles and/or long longs), stuff the Pool* into the first 4 bytes, and return the pointer 4 (or 8) bytes from the beginning of what you allocated. Then your globaloperator delete backs off the 4 (or 8) bytes, finds the Pool*, and if NULL, uses free() otherwise calls pool->dealloc(). The parameter passed to free()and pool->dealloc() would be the pointer 4 (or 8) bytes to the left of the original parameter, p. If(!) you decide on 4 byte alignment, your code would look something like this (although as before, the following operator new code elides the usual out-of-memory handlers):

void* operator new(size_t nbytes)
{
if (nbytes == 0)
nbytes = 1;                    
// so all alloc’s get a distinct address
void* ans = malloc(nbytes + 4);  
// overallocate by 4 bytes
*(Pool**)ans = NULL;             
// use NULL in the global new
return (char*)ans + 4;           
// don’t let users see the Pool*
}

void* operator new(size_t nbytes, Pool& pool)
{
if (nbytes == 0)
nbytes = 1;                    // so all alloc’s get a distinct address
void* ans = pool.alloc(nbytes + 4); 
// overallocate by 4 bytes
*(Pool**)ans = &pool;            
// put the Pool* here
return (char*)ans + 4;           
// don’t let users see the Pool*
}

void operator delete(void* p)
{
if (p != NULL) {
p = (char*)p – 4;              // back off to the Pool*
Pool* pool = *(Pool**)p;
if (pool == NULL)
free(p);                     
// note: 4 bytes left of the original p
else
pool->dealloc(p);            
// note: 4 bytes left of the original p
}
}

See more at http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s