The Utility Classes

Phish, like any substantial program, requires a certain amount of infrastructure support. In a large commercial application, this support will often be provided by a dedicated team of "tool builders". Many other utility classes can be purchased as part of a library of classes sold by third party vendors. The principal utility classes in Phish provide counted pointers and debugging support.

Counted Pointers

Counted pointers were described in the last chapter. They provide a more efficient use of memory and reduce the likelihood of memory leaks. Phish implements counted pointers with two classes:

RCBody

RCHandle

The handle/target idiom was described in detail in the counted pointer section in the last chapter. In this implementation, the RCHandle class is parameterized. This allows you to create a handle to any class derived from the target, RCBody, and allows you to treat these objects polymorphically through the handle.

You cannot use the handle to delete the object it holds, but you can dereference the handle as you might dereference a pointer, or you can call member functions through the handle. It is nearly impossible to inadvertently delete the object itself — you would have to write,

delete &(*ptr) 

or:

delete ptr.operator->();

Neither is a likely mistake. The implementation of the handle/target algorithm is fairly straightforward. The hard part is in managing the count of objects and writing the wrapper code so that clients can use the handle as if it were the object itself.

template<class T>
class RCHandle 
{
public:
   int compare ( const RCHandle &rhs ) const 
   {
      return compare(rhs.dBody);
   }

   int compare ( const RCBody *rhs ) const 
   {
      return ( rhs == dBody ? 0 : ( dBody > rhs ? 1 : -1 ) );
   }

    RCHandle ( RCBody *Body = 0)
      : dBody(Body)
    {   
       if ( dBody ) 
      {
         dBody->inc();
      }
    }
   
    RCHandle        ( const RCHandle &rhs )
      : dBody(rhs.dBody)
    {      
       if ( dBody ) 
      {
         dBody->inc();
      }
    }

    RCHandle &operator = ( const RCHandle &rhs )
    {
      if ( rhs.dBody ) 
      {
         rhs.dBody->inc();
      }

      if ( dBody ) 
      {
         dBody->dec()
      }

      dBody = rhs.dBody;
      return *this;
    }
    virtual ~RCHandle               ()
    { 
      if ( dBody ) 
      {
         dynamic_cast<RCBody&>(*dBody).dec();
      }
    }

    T*          operator->()          { return      asT();  }
    T&          operator* ()          { return     *asT();  }
    const T*    operator->()  const   { return      asT();  }
    const T&    operator* ()  const   { return     *asT();  }

    operator          bool()  const   { return dBody?true:false; }

    static void Swap ( RCHandle &lhs, RCHandle &rhs ) 
    {
      RCBody *Temp = lhs.dBody;
      lhs.dBody = rhs.dBody;
      rhs.dBody = Temp;
    }

    void Release ( void ) 
    {
      if ( dBody ) 
      {
         dBody->dec();
         dBody = 0;
      }
    }

private:
    RCBody *dBody;

    T* asT () 
    {   
      assert(dBody);
      return (T*)dBody;   
    }
    const T* asT()  const   
    {   
      assert(dBody);
      return (T*)dBody;   
    }
};

Note that the RCHandle contains a pointer to an RCBody. The asT private helper functions manage the casting and the accessor operators (that is, operator-> and operator*) return either a pointer or a reference to the held object. These accessors are overloaded for const and non-const, which allows the handle to be used as either a constant or non-constant pointer or reference. It is for such casting that the RCHandle is made a template.

The corresponding RCBody class is an abstract base class from which you will inherit any classes for which you want to be able to create handles. The code for this class is shown below:

class RCBody {
public:
    void inc()
    {
        assert(dRefCount>=0);
        dRefCount++;
    }
    bool dec()
    {
        assert(dRefCount>0);
        --dRefCount;
        if ( !dRefCount ) 
        {
            delete this;
            return true;
        }
        return false;
    }
protected:
    RCBody()
        :dRefCount(0)
    {
    }
    virtual ~RCBody() = 0
    {
        assert(!dRefCount);
    }
private:
    int dRefCount;

};

© 1998 by Wrox Press. All rights reserved.