/* * CENA C++ Utilities * * by Stephane Chatty * * Copyright 1990, 1991, 1992 * Laboratoire de Recherche en Informatique (LRI) * Centre d'Etudes de la Navigation Aerienne (CENA) * * smart pointers, originally by Michel Beaudouin-Lafon * * $Id$ * $CurLog$ */ #include #include #include "SmartPointer.h" CcuSmartData::check_type CcuSmartData::check = Warn; int CcuSmartData::NextCreatedIsDynamic = 0; /*?class CcuSmartData The class \typ{CcuSmartData} is the base class for objects that you want to reference through smart pointers. Such an object contains a reference count. This reference count contains the number of smart pointers to this object. When that number reaches 0 and the object was allocated dynamically, it is safe to destroy it (unless there are still plain pointers to it!). Hence, a \typ{CcuSmartData} destroys itself when its reference count reaches 0 and it was allocated dynamically (ie. with \fun{operator new}). To implement the reference counting properly, it is necessary to overload the assignment operator on the reference count, so that when object \var{a} is assigned to object \var{b}, the reference count of \var{b} does not change. This means that all classes deriving from \typ{CcuSmartData} will have their assignment operator implicitly redefined. This implies a small run-time overhead, and a special care if you need to overload this operator in a derived class of \typ{CcuSmartData}. Another point needs special attention. The only way of deciding whether an object was dynamically allocated is to redefine \fun{operator new}. This was done in \typ{CcuSmartData}. However, you might want to define your own \fun{new} and \fun{delete} in derived classes. If you do so, you should take care that \fun{new} sets the flag \var{CcuSmartData::NextCreatedIsDynamic} to a non-null value. ?*/ void* CcuSmartData :: operator new (unsigned long size) { NextCreatedIsDynamic = 1; return ::operator new (size); } /*? Create a data, ie. initialize its reference count to 0. ?*/ CcuSmartData :: CcuSmartData () : State (NextCreatedIsDynamic ? 0 : 1) { NextCreatedIsDynamic = 0; } /*? This is the copy constructor for the class \typ{CcuSmartData}. Because there is a copy constructor defined, all derived class will have a copy constructor defined implicitly, unless you specify one explicitely. ?*/ CcuSmartData :: CcuSmartData (const CcuSmartData&) : State (NextCreatedIsDynamic ? 0 : 1) { NextCreatedIsDynamic = 0; } /*? This is the destructor for class \typ{CcuSmartData}. It is {\em virtual}. This destructor checks that the deleted object has a null reference count. If not, it notifies the user according to the sanity check value defined with the static member function \fun{SetCheck}. Dynamically allocated \typ{CcuSmartData} objects are automatically deleted by the smart pointer package when their reference count reaches zero. ?*/ CcuSmartData :: ~CcuSmartData () { if (State > 1) { if (check >= OnlyDynamic) { if (IsDynamic ()) fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of dynamic object is %d !\n", this, int (State/2)); else if (check >= Warn) fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of global or automatic object is %d !\n", this, int (State/2)); } if (check == Abort) { fprintf (stderr, "*** aborting\n"); abort (); } } } void CcuSmartData :: DecrRef () { if (----State == 0) delete this; } #ifdef DOC /*? This protected member returns 1 if this object was allocated with \fun{operator new}. ?*/ int CcuSmartData :: IsDynamic () const { } #endif /*? This {\em static} member controls the sanity check done by the destructor of class \typ{CcuSmartData}: a \typ{CcuSmartData} should not be destroyed when its refcount is non zero, because this means that some smart pointers are still pointing at it. Such errors can happen in two situations:\\ \hspace*{0.5cm}1. when calling explicitely \fun{operator delete} on an object that has smart pointers to it;\\ \hspace*{0.5cm}2. when a local object (an object on the stack) that has smart pointers to it is automatically destroyed upon exit of its enclosing block.\\ \vspace{0.5ex} When an error occurs, the value of \var{chk} defines what happens, as follows:\\ \hspace*{0.5cm}$\bullet$ if \var{t} is \var{Warn}, a message is issued on \var{stderr} and processing continues;\\ \hspace*{0.5cm}$\bullet$ if \var{t} is \var{Abort}, a message is issued and and \fun{abort} is called, forcing a core dump;\\ but not for dynamically allocated objects.\\ \hspace*{0.5cm}$\bullet$ if \var{t} is \var{OnlyDynamic}, checking is disabled for global and automatic objects, \hspace*{0.5cm}$\bullet$ if \var{t} is \var{NoCheck}, checking is disabled.\\ \fun{SetCheck} returns the previous value of the checking status. The initial value is 0 (warning message). ?*/ CcuSmartData::check_type CcuSmartData :: SetCheck (check_type t) { check_type old = check; check = t; return old; } /*?class CcuSmartPointerTo The class \typ{CcuSmartPointerTo} is the smart pointer class itself. A \typ{CcuSmartPointerTo} object contains a pointer to a \typ{DATA} object. ?*/ #ifdef DOC /*? Construct a null smart pointer. ?*/ CcuSmartPointerTo :: CcuSmartPointerTo () { } /*? Construct a smart pointer to data \var{d}. \var{d} may be 0. ?*/ CcuSmartPointerTo :: CcuSmartPointerTo (DATA* d) { } /*? This is the copy constructor for smart pointers. It ensures that the reference counts are properly updated when a smart pointer is initialized by copy (argument passing and function return for instance). ?*/ CcuSmartPointerTo :: CcuSmartPointerTo (const CcuSmartPointerTo& p) { } /*? The destructor updates the reference count of the pointed to data, and destroys it if the reference count reaches 0 and the data was dynamically allocated. ?*/ CcuSmartPointerTo :: ~CcuSmartPointerTo () { } /*? This operator overloads the assignment of smart pointers. It can destroy the data pointed by the left-hand side pointer if its reference count reaches 0. ?*/ CcuSmartPointerTo& CcuSmartPointerTo :: operator = (const DATA* d) { } /*? This operator overloads the dereferencing of smart pointers. Unfortunately, it returns a \typ{DATA*} where one would prefer a pointer to a derived class of \typ{DATA}. This problem, which occurs also with the overloading operators below, is fixed by the generic pointer class (\fun{PointerClass}) described below. ?*/ DATA* CcuSmartPointerTo :: operator -> () { } /*? These conversion operators make it possible to pass a pointer to data where a smart pointer is expected. They also make it possible to test a smart pointer like a usual pointer. ?*/ CcuSmartPointerTo :: operator DATA* () { } // note: this is hacked up for the doc stuff /*? This is a macro to generate a smart pointer class. \typ{SmartClass} is the name of the class to generate, and \typ{DataClass} is the name of the class to which \typ{SmartClass} objects will point. \typ{DataClass} must derive from \typ{DATA}. The generated class is similar to the class \typ{CcuSmartPointerTo} described above, with \typ{CcuSmartPointerTo} replaced by \typ{SmartClass} and \typ{DATA} replaced by \typ{DataClass}. In particular, this means that such smart pointers can be dereferenced with the \fun{operator ->} like usual pointers. ?*/ generic PointerClass (SmartClass, DataClass) { } /*? This macro is similar to \fun{PointerClass} described above. It generates a smart pointer class named \typ{SmartClass} for the class \typ{DataClass}. Unlike the previous macro, the generated class is not a base class, but instead a derived class of \typ{SmartBaseClass}, which must be a smart pointer class itself. If \typ{pA} is a smart pointer class to class \typ{A} and \typ{B} derives from \typ{A}, you can declare a smart pointer class \typ{pB} to the class \typ{B} with:\\ \hspace*{1cm}\com{DerivedPointerClass (pB, pA, A)}\\ Then \typ{pB} objects can be used where \typ{pA} objects are expected, which would not be the case if \typ{pB} was declared with \fun{PointerClass}. ?*/ generic DerivedPointerClass (SmartClass, BaseSmartClass, DataClass) { } #endif