Jump to content

More C++ Idioms/Concrete Data Type

From Wikibooks, open books for an open world

Concrete Data Type

[edit | edit source]

Intent

[edit | edit source]

To control object's scope and lifetime by allowing or disallowing dynamic allocation using the free store (heap)

Also Known As

[edit | edit source]

Motivation

[edit | edit source]

C++ provides two ways of controlling lifetime of an object and binding it with a program level identifier (variable). First is a scoped variable and an object, which are destroyed immediately after the scope ends (e.g., function scope integers). Second is a scoped variable (often a pointer) and a dynamically allocated object in the free store. In this case, at the end of the scope of the variable, the variable ceases to exist but the object's lifetime continues (e.g., singletons, a window object). It is possible to force the choice of the lifetime of an object either first way or the second using the Concrete Data Type idiom.

Solution and Sample Code

[edit | edit source]

This idiom simply uses class level access modifiers (private, protected) to achieve the goal. The following code shows how a MouseEventHandler class forces dynamic allocation.

class EventHandler 
{
  public:
    virtual ~EventHandler () {}
};
class MouseEventHandler : public EventHandler // Note inheritance
{
  protected:
    ~MouseEventHandler () {} // A protected virtual destructor.
  public:
    MouseEventHandler () {} // Public Constructor.
};
int main (void)
{
  MouseEventHandler m; // A scoped variable is not allowed as destructor is protected.
  EventHandler *e = new MouseEventHandler (); // Dynamic allocation is allowed
  delete e;  // Polymorphic delete. Does not leak memory.
}

Another way to force dynamic allocation is to prevent direct access to the constructor and instead provide a static function instance() to return a dynamically allocated object. It is in many ways similar to the Singleton design pattern. Moreover, it is not strictly necessary to use polymorphic delete to reclaim memory. A member function destroy() can serve the purpose saving the space required for a v-table pointer.

class MouseEventHandler // Note no inheritance
{
  protected:
    MouseEventHandler () {} // Protected Constructor.
    ~MouseEventHandler () {} // A protected, non-virtual destructor.
  public:
    static MouseEventHandler * instance () { return new MouseEventHandler(); }
    void destroy () { delete this; }  // Reclaim memory.
};

An opposite extreme of this idiom is to force scoped variable (a.k.a. automatic variable) only. It can be achieved using a private new operator.

class ScopedLock
{
  private:
    static void * operator new (size_t size); // Disallow dynamic allocation
    static void * operator new (size_t, void * mem);  // Disallow placement new as well.
};
int main (void)
{
   ScopedLock s; // Allowed
   ScopedLock * sl = new ScopedLock (); // Standard new and nothrow new are not allowed.
   void * buf = ::operator new (sizeof (ScopedLock));
   ScopedLock * s2 = new(buf) ScopedLock;  // Placement new is also not allowed
}

ScopedLock object can't be allocated dynamically with standard uses of new operator, nothrow new, and the placement new.

Known Uses

[edit | edit source]
[edit | edit source]

References

[edit | edit source]