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