Index

NAME

Handle(ClassType) - smart pointer for class ClassType


INCLUDE

include "Handle_ClassType_h.h"

SYNTAX

 class Handle(ClassType)
 {
 protected:
   ClassType* classptr;    // the only data item
   bool checkPtr () const;

 public:
   Handle(ClassType) ();
   Handle(ClassType) (const ClassType& p);
   Handle(ClassType) (ClassType* p);
   Handle(ClassType) (const Handle(ClassType)& p);
  ~Handle(ClassType) ();

   void rebind (const ClassType* pc);
   void rebind (const ClassType& p);
   Handle(ClassType)& detach ();                 // same as rebind(NULL)

   void operator = (const Handle(ClassType)& h); // avoid this one
   void operator = (const ClassType& p);
   void operator = (const ClassType* p);

   bool ok () const;
   bool ok (const char* message) const;

   const ClassType* operator -> () const;
   const ClassType& operator () () const;
   const ClassType& operator *  () const;
   const ClassType* getPtr () const;
   const ClassType& getRef () const;

   ClassType* operator -> ();
   ClassType& operator () ();
   ClassType& operator *  ();
   ClassType* getPtr ();
   ClassType& getRef ();

   ClassType** getPtrAdr ();

   bool operator == (const Handle(ClassType)& h) const;
   bool operator != (const Handle(ClassType)& h) const;
   bool operator <  (const Handle(ClassType)& h) const;
   bool operator >  (const Handle(ClassType)& h) const;
 };



KEYWORDS

smart   pointer,  memory  management,  allocation,  deallocation,
delete



DESCRIPTION

The class provides a smart pointer to class objects.  The  advan­
tage  of  using  handles instead of standard C++ pointers is that
the memory management of (large) objects is significantly simpli­
fied and the safety of memory operations is increased.  The docu­
ment "Smart Pointers" gives information on the use and purpose of
handles.



CONSTRUCTORS AND INITIALIZATION

The  empty  constructor  sets  the pointer in the Handle to NULL.
When using this constructor the programmer must later  assign  an
object  to  the  Handle  by  using  the  rebind  member function.
Another constructor takes a ClassType  pointer  as  argument  and
sets  the pointer in the Handle to point to the ClassType object.
There is also a copy constructor.


MEMBER FUNCTIONS

ok - is false if the pointer in the  Handle  is  a  NULL  pointer
(that is, the Handle does not point to any object).

empty  -  is  true  if  the  Handle  does  not point to an object
(empty() equals !ok()).

operator-> - gives the normal pointer syntax for the Handle, that
is,  a  pointer  to  the  class  object is returned (see examples
below).  The functions that return reference or  pointer  to  the
class  object  have different checks on NULL pointers. The opera­
tor-> function tests on a NULL pointer if the program is compiled
in nopt mode (plain Make or Make MODE=nopt). In optimization mode
(opt) it returns a NULL pointer without any warning. To check  if
the  handle  is empty, use ok or empty. In the case you work with
NULL pointers in handles and do not want  to  be  interrupted  by
error  messages, not even in nopt mode, use getPtr, and stay away
from getRef.

operator* - returns a reference to the object that is handled  by
the  Handle.  Tests on NULL pointer follow the strategy in opera­
tor->.

operator() - returns a reference to the object that is handled by
the  Handle.  Tests on NULL pointer follow the strategy in opera­
tor->.  Note that both x() and *x dereferences  a  handle  x  (in
fact,  x.getRef()  does the same job so there are three choices).
In the first versions of Diffpack, only the x() syntax  was  sup­
ported  (along  with x.getRef()). Since some users have found the
use of operator() strange  for  an  operation  that  is  normally
defined  by  operator*  in  C and C++, we have also included this
more standard syntax as a part of the handle class.  The  genesis
of  operator() was that handle should be distinguished from ordi­
nary pointers (however, this idea was not  completely  consistent
since operator-> was used).

getPtr  -  returns a pointer, similar to operator->. The function
never checks that the pointer is valid, that is, that the pointer
is  not  a NULL pointer (not even in nopt compilation mode). This
is the only function that should be used for  dealing  with  NULL
pointers (getRef etc may give warning/error messages in nopt com­
pilation mode).

getRef - returns a reference, similar to operator().  This  func­
tion  always checks that the pointer, corresponding to the refer­
ence that is returned, is not a NULL pointer. If a  NULL  pointer
is detected, an error message is issued.

Note  that there are three possibilities to extract the reference
of an X object handled by a Handle(X) object x:  x(),  x.getRef()
and  *x.  All  these are equivalent. They should only be used for
non-NULL pointers. Use x.getPtr() if you want the code to  possi­
bly deal with a NULL pointer in x.

rebind  - lets the pointer in the Handle point to a (new) object.
The function will automatically count down the number  of  refer­
ences to its present object and delete the object if there are no
more references. The  rebind  function  is  optimized  such  that
rebinding  to  its  present object is detected and no actions are
performed.

Note that object which have handles   to  each  other  should  be
avoided.  We  call this circular handles. For example, if A has a
handle to a B object, and B has a  handle  to  A,  the  reference
counting  procedure  will  not  be  correct when both A and B are
dynamically allocated (i.e. new is called  explicitly).  In  such
cases,  one  must  declare  one of the handle  classes using Han­
dleCh.h (and not simply Handle.h). The compiler will  give  error
message  until  the  declaration  is  correct. Such circumstances
should be avoided. Let instead, e.g., B have an ordinary  pointer
to  A. This is not as safe as a handle, but memory leakage can be
avoided. In the case where at least one of the  objects  involved
in circular handles is allocated statically, circular handles are
expected to work as intended.

detach - same as rebind(NULL), except that  it  allows  the  nice
syntax (if h is a handle):

 h.detach().rebind (new H(3.5));

This  statement  can  be  advantageous if h is pointing to a very
large object (e.g., from the previous run in a multiple loop) and
the new H object is also very large. If one writes

 h.rebind (new H(3,5));

the  new  H object will first be made, and then the object that h
points to will be deleted. Two large objects may therefore  exist
simultaneously, although only for a very short time, but this may
require too much memory. Just  insert  a  detach  call  to  first
ensure that the object is deleted (assuming that only h is point­
ing to the object) before a new object is allocated.

getPtrAdr - returns the address of  the  pointer.  Enables  other
objects  to  have  a  pointer  that "follows" the handle pointer.
Typical applications are the following. Assume that a base  class
X  has a subclass Y that contains a handle to a class with a name
that is unknown for X (for example if X is a general class and  Y
is a user dependent code class). Let the handle in Y be Handle(S)
where S has a base R.  Then one can have a R** in X that  is  set
to Handle(S)::getPtrAdr() in the constructor. The R** will follow
the Handle(S) without knowing the exact handle type. However, R**
can be used to invoke virtual functions of the S hierarchy!


DEVELOPED BY

SINTEF Applied Mathematics, Oslo, Norway, and University of Oslo,
Dept. of Mathematics, Norway


AUTHOR

Hans Petter Langtangen, SINTEF/UiO