Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Kylix Kicks
 Borland C++Builder lost+found #12
See Also: C++Builder Papers and Columns

That's me over there
Last column I gave an overview of use counting. It took us along what I learned on that subject from Andrew Koenig while he did two presentations at C++ World Conference 1999 in Miami, Florida. That was not the only subject he presented, actually, he mixed use counting with a templatized version of handle-body idiom. In this installment I'll run you through what Andrew told us about that. You might want to have the contents of lost+found 11 handy. I am using the Use class as outlined in that lost+found installment.

Introduction
Coplien tells us in 'Advanced C++ Programming Styles and Idioms' that the handle-body idiom 'may be used to decompose a complex abstraction into smaller, more manageable classes' and that it 'may reflect the sharing of a single resource by multiple classes'. Finally, he concludes that 'the most common case is for reference counting'. In lost+found 11 you have seen a more or less classic approach to this idiom. Another method for reference counting is using counted pointers. An example is:

  class ObjRef
  {
  public:
    ObjRef(Object* objptr = 0)
    : ptr(objptr), use()
    {}
    ObjRef(const ObjRef& that)
    : ptr(that.ptr), use(that.use)
    {}
    ObjRef& operator=(const ObjRef& that)
    {
      if (this != &that)
      {
        if (use.Unique() == true) delete ptr;
        use = that.use;
        ptr = that.ptr;
      }
      return *this;
    }
    ~ObjRef()
    {
      if (use.Unique() == true) delete ptr;
    }

    //----------------------------------------------------------
    //  Magic tricks!
    Obj& operator*() const {return *ptr;}
    Obj* operator->() const {return ptr;}

    //----------------------------------------------------------
    //  I want my own Object!
    void MakeUnique()
    {
      if (use.Unique() == false)
      {
        use = Use();
        ptr = new Object(*ptr);
      }
    };
  private:
    Obj* ptr;       //  The object referred to
    Use  use;        //  lost+found 11
  };
Magic involved
The magic in counted pointers lies in the overloaded operators. They return a reference or a pointer to the object referred to. That is why you can use the interface of the whole class through the counted pointer without having to duplicate the interface in the counted-pointer class, as opposed to the reference counting idiom, where you must do that by hand. If for example the Object class has a member DoOne, then you can reach it as follows:
  ObjRef or(new Object);

  or->DoOne();
If you later decide to add DoTwo, you do not need to add DoTwo to the ObjRef class, but simply use:
  or->DoTwo();
The only funny thing is that the use of ObjRef looks like a normal object, but when using it it looks like a pointer.

Templatizing it
A counted pointer is very suited to be templatized. Here is how:

  template  class CountedPointer
  {
  public:
    CountedPointer(T* objptr = 0)
    : ptr(objptr), use()
    {}
   CountedPointer (const CountedPointer& that)
    : ptr(that.ptr), use(that.use)
    {}
   CountedPointer& operator=(const CountedPointer& that)
    {
      if (this != &that)
      {
        if (use.Unique() == true) delete ptr;
        use = that.use;
        ptr = that.ptr;
      }
      return *this;
    }
    ~ CountedPointer ()
    {
      if (use.Unique() == true) delete ptr;
    }

    //----------------------------------------------------------
    //  Magic tricks!
    T& operator*() const {return *ptr;}
    T* operator->() const {return ptr;}

    //----------------------------------------------------------
    //  I want my own Object!
    void MakeUnique()
    {
      if (use.Unique() == false)
      {
        use = Use();
        ptr = new T(*ptr);
      }
    }
  private:
    T* ptr;         //  The object referred to
    Use  use;       //  lost+found 11
  };
Drawback
The big drawback is that we can use the template only for a single type. That means that the following will go wrong:
  CountedPointer cpb = new CountedPointer(new Base);
  CountedPointer cpd = new CountedPointer(new Derived);
What happens if we execute:
  cpd.MakeUnique();
What happens is that now the cpd points to a sliced copy of the Derived object we originally put in the CountedPointer, since it executes ptr = new T(*ptr). Since T is of type Base here, we will call new Base instead of new Derived.

First solution
The first solution to the drawback is to introduce a Clone member in the class referred to:

  class Base
  {
  public:
    virtual Base* Clone() const {return new Object(*this);}
  };

  class Derived: public Base
  {
  public:
    virtual Base* Clone() const {return new Derived(*this);}
  };
However, that means that you almost always must specialize Clone for every class in the class hierarchy. If you forget to do so, the object will still be sliced because the virtual member of one of the base classes in the hierarchy is called. That defies the non-intrusiveness of the Use class, and, as noted in lost+found 11, you sometimes do not have the means to modify a class.

Second solution
The second solution is to provide a Clone template:

  template  T* Clone(const T* p)
  {
    return p->Clone();
  }
We could have used:
  template  T* Clone(const T* p)
  {
    return new T(*p);
  }
but that is dangerous. If we forget to specialize the Clone template then we will get slicing behavior again without any error messages.

We have to adjust the MakeUnique member in this case:

  //----------------------------------------------------------
  //  I want my own Object!
  void MakeUnique()
  {
    if (use.Unique() == false)
    {
      use = Use();
      ptr = Clone(ptr);
    }
  }
This solution is better, because we can specialize the template for those objects that do not have a Clone member themselves:
  template <> Derived* Clone(Derived* p)
  {
    //  Derived does not have a Clone
    return new Derived(*p);
  }
The compiler will warn us if a class does not have the proper Clone member to use. That's a sure sign we must specialize the template function. However, we might end up with a lot of template specializations to make this work. In that case, using a macro to generate the template function specializations might help:
  #define SPECIALCLONE(Type)\
  template <> Type * Clone(Type * p)\
  {\
    return new Type (*p);\
  }

This webpage © 2000-2017 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.