Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Monday, April 24, 2006

C++ Forcing a certain storage for your objects and 'delete this'

Forcing a certain storage for your objects

Originally posted at http://www.ciprian-miclaus.com/tips/enforce.asp?tip=b&id=3 (editted formatting)

In general we don't care if our classes get instantiated on the stack or on the free store (the storage where new/delete allocate memory vs. heap which is used to denote the storage used malloc/free). But there are cases when we would like our classes to be instantiated only on the stack or only on the free store. Why would we want something like this? Let's say we end up with a pointer to an object which we own. Should we or should we not delete it? If the object is on the free store and we do not delete it, we end up with memory leaks. However, if the object is on the stack and we're trying to delete it, we'll obviously end up with a splendid crash.

As a rule of thumb, whoever creates an object should destroy it too, so the whole thing about where it was created is generally not a problem. However, there are cases when parameters are passed asynchronously and they're usually created on one side and destroying something else, maybe in different threads. Then usually we'll want to allocate parameters on the free store. But we'll also want to enforce that they should be allocated only on the free store as one might allocate them on the stack and the parameters might be destroyed by the creating-end before they're examimded by the other end.

So, such enforcements of the storage used by certain classes are not totally futile. Let's see how can we enforce in C++ that a class should be instantiated only on the stack or only on the free store. This is about how and where objects are created. So we're probably want to take a look at areas like constructors, destructors, operator new, operator delete, copy constructor, because those are the places where an object of a certain type gets constructed. We also have the access specifiers: public, protected, private. By playing with these toys, we should achieve our purpose.

Forbidding a class to be instantiated on the free store is simple. Just make the operator new and delete protected or private, and client code will not be able to call them, so they will be enforced to instantiate it on the stack.


class OnlyOnStack {
public:
//constructor
OnlyOnStack ();
//destructor
~OnlyOnStack ();

//..public interface here

private:
operator new (size_t) {}
operator delete (void*) {}

};
The choice between protected and private it's the choice between letting your subclasses instantiate on the free store or not. Usually you'll make them private, as subclasses should abide the rules too, but depends on the implementation. If you don't know which is good for you, then choose private.

Forbidding a class to be instantiated on the stack seems simple too. If we simple make the constructors protected or private, we will not be able to instantiate it on the stack. Simple enough. Not quite. When a class gets instantiated on the free store, two things happens:


1. operator new is called (if the class doesn't have an operator new,
then the global operator new is used) that allocates memory for the object
2. the constructor is called
Obviously if the constructor is protected or private, this 2 steps process will fail, and the compiler will signal such a failure fortunately at the compile time. operator new must be public, so what else we got? Destructor. What option? Make it protected or private. Ok, let's see what happens:

class OnlyOnFreeStore {
public:
//constructor
OnlyOnStack ();

//..public interface here

private:
//destructor
~OnlyOnStack ();

};
If we try to instantiate it on the stack with a simple:

OnlyOnFreeStore trivialInstance;
the compiler will complain because when the variable trivialInstance goes out of scope and it should call the destructor, it discovers it cannot as you made the destructor protected or private. Well done! You enforce it not to be on the stack. How about the free store? We'll notice that

OnlyOnFreeStore* pOnlyOnFreeStore = new OnlyOnFreeStore;
it's perfectly legal and the compiler doesn't complain about it. Indeed, it doesn't have any reasons to complain: the constructor is public, and it'll use the global operator new as we haven't defined a custom operator new for this class. How about:

delete pOnlyOnFreeStore;
Well, what happens is:
1. Compiler calls the destructor to destruct the object
2. Compiler calls operator delete the memory allocated for the object
Pretty fair. We've constructed it in 2 steps, we're destroying it in 2 steps. But the destructor is protected or private, so it cannot call it. Here the compiler will complain at compile time (fortunatelly). We cannot give up destructor made protected or private, it's our last hope. Instead let's add to the public interface a method for object destruction.

class OnlyOnFreeStore {
public:
//constructor
OnlyOnStack ();

DeleteThis () {
delete this;
}
//..public interface here

private:
//destructor
~OnlyOnStack ();

};
DeleteThis simply calls delete this, as it can access the private destructor, so everything will work fine. Calling delete this from a member method is however dangerous for at least two reasons:

1. From inside the method we cannot tell if this it's on the stack or on the free store which brings us to the beginning of this tip (we've assure our object is on the free store all the time)

2. After calling DeleteThis the object is destroyed and you cannot use it (it's equivalent to delete pOnlyOnFreeStore) just that users are not familiar with this technique as opposed to the classical delete pointer, so the technique it's error-prone.

However, this is our solution. So we'll use our class like this:

OnlyOnFreeStore* pOnlyOnFreeStore = new OnlyOnFreeStore;
//...use the object here
pOnlyOnFreeStore->DeleteThis ();
//the object is destroyed
Mission accomplished...