/* * CENA C++ Utilities * * by Stephane Chatty * * Copyright 1990-1995 * 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 #ifdef __GNUG__ #pragma implementation "SmartPointer.h" #endif #include "SmartPointer.h" #include "List.h" CcuSmartData::check_type CcuSmartData::check = doWarn; #ifdef OLD int CcuSmartData::NextCreatedIsDynamic = 0; #else CcuList* CcuSmartData::LastDynamics; #endif /*?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. ?*/ /*! One big problem with smart pointers lies in making a distinction between objects created in the stack (that we should not delete) and objects created dynamically, that we should delete when no more referenced. There's no way to make that distinction in constructors, sowe try to use operator new. This works well only if the constructor is called right after operator new, which is not always the case... !*/ void* CcuSmartData :: operator new (size_t size) { #ifdef OLD NextCreatedIsDynamic = 1; return ::operator new (size); #else int sz = (int) size; void* p = ::operator new (sz); if (!LastDynamics) LastDynamics = new CcuList; LastDynamics->Prepend (p); return p; #endif } /*? Create a data, ie. initialize its reference count to 0. ?*/ CcuSmartData :: CcuSmartData () #ifdef OLD : State (NextCreatedIsDynamic ? 0 : 1) #else : State (1) #endif { #ifdef OLD NextCreatedIsDynamic = 0; #else if (LastDynamics && LastDynamics->Remove (this)) State = 0; #endif } /*? 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&) #ifdef OLD : State (NextCreatedIsDynamic ? 0 : 1) #else : State (1) #endif { #ifdef OLD NextCreatedIsDynamic = 0; #else if (LastDynamics && LastDynamics->Remove (this)) State = 0; #endif } /*? 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 >= doOnlyDynamic) { if (IsDynamic ()) fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of dynamic object is %d !\n", this, int (State/2)); else if (check >= doWarn) fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of global or automatic object is %d !\n", this, int (State/2)); } if (check == doAbort) { fprintf (stderr, "*** aborting\n"); abort (); } } } void CcuSmartData :: DecrRef () { State -=2; 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{doWarn}, a message is issued on \var{stderr} and processing continues;\\ \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doAbort}, 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{doOnlyDynamic}, checking is disabled for global and automatic objects, \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doNoCheck}, 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