Restrict Object Allocation to Specific Memory Types

C++ inherited from C its three memory storage types: automatic storage (also called stack memory), static storage for namespace-scope objects and local static objects, and the free-store(also called the heap), which is used for dynamically-allocated objects.

In many cases, this diversity complicates things. For example, in mobile platforms such as Symbian, free-store objects are mandatory due to the system’s tiny stack size. Likewise, garbage collectors and smart-pointers require that objects be allocated on the free-store exclusively. But in other cases, you want to ensure that objects are neverallocated on the free-store. The following sections show how to enforce such class-specific memory usage policies.

How do you ensure that objects of a certain class are allocated on the free-store exclusively? Similarly, how can you encourage automatic and static storage types for a given class while disabling free-store allocation?

Enforce memory allocation policies by controlling the access type of a class’s member functions.

Disabling Static and Automatic Objects
Smart pointer classes such as auto_ptrmust own objects that were allocated on the free-store. Alas, the compiler doesn’t detect violations of this restriction:

#include #include int main (){ std::ofstream myfile;std::auto_ptr file_ptr(&myfile);//disastrous!}

auto_ptr’s destructor calls delete to destroy myfile, although the latter was allocated on the stack. The result is undefined behavior. You can enforce this compile-time constraintby declaring the destructor as a private member:

class HeapOnly{private: ~HeapOnly();//auto and static objects are disabledpublic: //..};

How does this work? The compiler implicitly invokes the destructors of automatic and static objects when they go out of scope or when the program terminates, respectively. If the destructor isn’t public, you get a compilation error, which is exactly what you want:

HeapOnly h1;//compilation error:          //"Destructor for 'HeapOnly' is not accessible"int main{ HeapOnly h2; // ditto static HeapOnly h3; //ditto}

You can create such objects on the free-store. This raises yet another question: how do you destroy them? The solution is to delegate the destruction to another public member function:

class HeapOnly{private: ~HeapOnly();public: void destroy() {delete this;} //invoke the destructor};HeapOnly * p = new HeapOnly;//..use pp->destroy()

Admittedly, this technique isn’t perfect because you still can’t bind auto_ptr to such an object. This time the problem is that auto_ptr’s destructor can’t delete p. However, it shouldn’t be difficult to write an elementary smart pointer class whose destructor contains:


Disabling Allocation on the Free-store
It’s possible to define a class whose objects may be created on the stack or static memory but not on the free-store. For this purpose, override global new and deleteby declaring them as non-public class members:

class AutoStatic{private: void * operator new (size_t)                      {return 0;} //dummy implementation void operator delete (void*) {}public: //..};

The compiler will use the overridden versions of new and deleteto allocate and destroy objects of this class. Because these operators are inaccessible, any attempt to allocate such objects on the free-store would cause compilation errors:

int main(){ AutoStatic as; //fine static AutoStatic as2; //fine AutoStatic * p2= new AutoStatic; // error:    //'AutoStatic::operator new(unsigned int)'    //is not accessible  delete p2;//error:           //'AutoStatic::operator delete(void *)' is not                       //accessible.}

There is a loophole in this design, though. AutoStatic doesn’t override the array versions of new and delete. If you wish to disable dynamic allocation of AutoStaticarrays, override these operators as well:

class AutoStatic{private: //override global new[] and delete[] void * operator new [](size_t);  void operator delete [] (void*);//..};

Design Refinements
In cross-platform code, you should provide dummy definitions for the overriding new and deletebecause some implementations may call them implicitly from constructors and destructors.

Notice, also, that in a class hierarchy, a derived class uses the overridden new and deleteof its base class, unless it declares its own overriding versions of these operators.

A Walk Down Memory Lane
Seasoned programmers can find ways to bypass the constraints that I’ve shown here. For example, one could use placement new to construct objects on a buffer that was allocated from the free-store. However, a bullet-proof design isn’t the issue here. Rather, the aim is to protect your code from innocent human errors and to draw users’ attention to the memory-usage policy of a given class. In this respect, making a destructor non-public is sufficient for disabling static and automatic objects. Similarly, declaring new and deleteas private or protected class members is also an effective mechanism for disabling free-store allocation.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts