1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | #ifndef STLPLUS_SIMPLE_PTR #define STLPLUS_SIMPLE_PTR //////////////////////////////////////////////////////////////////////////////// // Author: Daniel Milton // Copyright: (c) Daniel Milton 2002 onwards // License: BSD License, see ../docs/license.html // A smart pointer is a memory-managing pointer to an object. If you like, it // is a zero-dimensional container. // Assignment of smart pointers result in multiple aliases of the same object. // The term alias is used to differentiate from conventional pointers because // the semantics are different. // Aliases can be turned into copies if the pointed-to class supports copying. // These simple_ptr classes from DJDM have slightly different semantics than // the smart_ptr classes of AJR. There are no cross-pointer side effects // that occur when the pointer is cleared. The clear() function is effectively // equivalent to the clear_unique() function of the smart_ptr. The only way // that a "referenced" object will be deleted is if all simple_ptr's that // reference the object are cleared (by deletion, manual clearing or reassignment). // Also, the simple pointer cannot contain a reference to a shared null pointer // (which occurs as a side-effect of clearing a multiply referenced object in // the smart_ptr classes). Which means that if you have a null simple_ptr, then // the assignment of any other null simple_ptr will NOT reassign the reference of // any other simple_ptr. Hence, the simple_ptr class acts a little more like a // normal pointer (with fewer side effects), with the added bonus of containment. // Due to the way that the simple_ptr contains the data, it also allows the // addition of various casting functions, while still keeping the managed data // containment functionality of the underlying object. This means that you can // have two simple_ptr's of different template types, both pointing to the same // data (if the differing template types are derivatives of each other). // The base class is simple_ptr_base which defines the common interface. Then // there are three subclasses which have the same interface but different copy // semantics: // - simple_ptr for simple types and classes which have copy constructors // - simple_ptr_clone for polymorphic class hierarchies which are copied using a clone method // - simple_ptr_nocopy for any class that cannot or should not be copied //////////////////////////////////////////////////////////////////////////////// #include "containers_fixes.hpp" #include "exceptions.hpp" #include "copy_functors.hpp" #include <map> #include <string> namespace stlplus { //////////////////////////////////////////////////////////////////////////////// // Base class //////////////////////////////////////////////////////////////////////////////// template < typename T, typename C> class simple_ptr_base { public : ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; typedef C value_copy; ////////////////////////////////////////////////////////////////////////////// // constructors and destructors // create a null pointer simple_ptr_base ( void ); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form smart_ptr_base<type> x(new type(args)) explicit simple_ptr_base (T* data); // copy constructor implements aliasing so no copy is made // note that the copy constructor should NOT be explicit, as this breaks // the returning of pointer objects from functions (at least within GCC 4.4) simple_ptr_base ( const simple_ptr_base <T,C>& r); // assignment operator - required, else the output of GCC suffers segmentation faults simple_ptr_base <T,C>& operator =( const simple_ptr_base <T,C>& r); // destructor decrements the reference count and delete only when the last reference is destroyed ~ simple_ptr_base ( void ); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool ( void ) const ; bool operator !( void ) const ; bool present( void ) const ; bool null( void ) const ; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 // exceptions: null_dereference T& operator *( void ) ; // exceptions: null_dereference const T& operator *( void ) const ; // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() // exceptions: null_dereference T* operator ->( void ) ; // exceptions: null_dereference const T* operator ->( void ) const ; ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // get the value // exceptions: null_dereference T& value( void ) ; // exceptions: null_dereference const T& value( void ) const ; // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set (T* data = 0); // get the pointer T* pointer( void ); const T* pointer( void ) const ; ////////////////////////////////////////////////////////////////////////////// // functions to manage aliases // make this an alias of the passed object void alias( const simple_ptr_base <T,C>&); // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases( const simple_ptr_base <T,C>&) const ; // find the number of aliases - used when you need to know whether an // object is still referred to from elsewhere (rare!) unsigned alias_count( void ) const ; // clear the reference to the object, but only delete the object if there are no // other references to that object. Hence, this does not affect other pointers // that are pointing to the same object. void clear( void ); // This is just an alias of the clear() function, provided for completeness of // the interface when acting as a replacement for the smart_ptr classes void clear_unique( void ); ////////////////////////////////////////////////////////////////////////////// // functions that involve copying // these functions use the copy functor passed as the template parameter C // to copy the object with the right copy semantics. If the copy functor // is no_copy, an exception will be thrown. // create a pointer containing a *copy* of the object using the template parameter C // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // exceptions: illegal_copy explicit simple_ptr_base ( const T& data) ; // set the value - note that this does a copy using the C template parameter // exceptions: illegal_copy void set_value( const T& data) ; // make this pointer unique with respect to any other references to the same object // if this pointer is already unique, it does nothing - otherwise it copies the object // exceptions: illegal_copy void make_unique( void ) ; // make this pointer a unique copy of the parameter // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 // exceptions: illegal_copy void copy( const simple_ptr_base <T,C>&) ; ////////////////////////////////////////////////////////////////////////////// protected : T* m_pointer; unsigned * m_count; public : // internal use only - had to make them public because they need to be // accessed by routines that could not be made friends // can't have a handle due to the way the simple pointer stores it's data // in separate counter and pointer objects unsigned * _count( void ) const ; T* _pointer( void ) const ; void _make_alias(T* pointer, unsigned * const & count); private : inline void increment( void ); inline bool decrement( void ); inline void _delete_pointer( void ); }; //////////////////////////////////////////////////////////////////////////////// // simple_ptr for simple types and classes which have copy constructors template < typename T> class simple_ptr : public simple_ptr_base <T, constructor_copy <T> > { public : simple_ptr ( void ) {} explicit simple_ptr ( const T& data) : simple_ptr_base <T, constructor_copy <T> >(data) {} explicit simple_ptr (T* data) : simple_ptr_base <T, constructor_copy <T> >(data) {} simple_ptr <T>& operator =( const T& data) { this ->set_value(data); return * this ;} simple_ptr <T>& operator =(T* data) { this -> set (data); return * this ;} ~ simple_ptr ( void ) {} #ifdef STLPLUS_MEMBER_TEMPLATES // functions that involve casting // moved from base class for two main reasons, though the second is a feature of the first: // 1. GCC cannot cast the previous base result of simple_ptr_base<T2, constructor_copy<T> > // as a simple_ptr<T2> even though it used to look like a duck and quack like a duck. // I think it was really complaining that the copy class was not guaranteed to be the same. // 2. Within the cast routines, one pointer type tried accessing private data of the other // pointer type and even though they are really the same type, was not allowed. Because // of this, the "private" function _make_alias is utilised to get the same result. // By having the cast functions in each derived class, you are guaranteed to use the same // copy class - no question. GCC is ok with this. template < typename T2> simple_ptr <T2> dyn_cast( void ) const ; template < typename T2> simple_ptr <T2> stat_cast( void ) const ; template < typename T2> simple_ptr <T2> cast( void ) const ; #endif }; //////////////////////////////////////////////////////////////////////////////// // simple_ptr_clone for polymorphic class hierarchies which have a clone method template < typename T> class simple_ptr_clone : public simple_ptr_base <T, clone_copy <T> > { public : simple_ptr_clone ( void ) {} explicit simple_ptr_clone ( const T& data) : simple_ptr_base <T, clone_copy <T> >(data) {} explicit simple_ptr_clone (T* data) : simple_ptr_base <T, clone_copy <T> >(data) {} simple_ptr_clone <T>& operator =( const T& data) { this ->set_value(data); return * this ;} simple_ptr_clone <T>& operator =(T* data) { this -> set (data); return * this ;} ~ simple_ptr_clone ( void ) {} #ifdef STLPLUS_MEMBER_TEMPLATES // functions that involve casting // moved from base class - see simple_ptr above template < typename T2> simple_ptr_clone <T2> dyn_cast( void ) const ; template < typename T2> simple_ptr_clone <T2> stat_cast( void ) const ; template < typename T2> simple_ptr_clone <T2> cast( void ) const ; #endif }; //////////////////////////////////////////////////////////////////////////////// // simple_ptr_nocopy for any class that cannot or should not be copied template < typename T> class simple_ptr_nocopy : public simple_ptr_base <T, no_copy <T> > { public : simple_ptr_nocopy ( void ) {} explicit simple_ptr_nocopy (T* data) : simple_ptr_base <T, no_copy <T> >(data) {} simple_ptr_nocopy <T>& operator =(T* data) { this -> set (data); return * this ;} ~ simple_ptr_nocopy ( void ) {} #ifdef STLPLUS_MEMBER_TEMPLATES // functions that involve casting // moved from base class - see simple_ptr above template < typename T2> simple_ptr_nocopy <T2> dyn_cast( void ) const ; template < typename T2> simple_ptr_nocopy <T2> stat_cast( void ) const ; template < typename T2> simple_ptr_nocopy <T2> cast( void ) const ; #endif }; //////////////////////////////////////////////////////////////////////////////// // internally, simple_ptr allocates lots of count values, which are just // unsigned reference counts. The allocation of these values benefit from // being allocated from a dedicated pool, as this increases the speed // therefore I have now provided the ability to override the memory management // of these values without needing to override the global new/delete // all you need to do is to define these functions BEFORE including this file // and set the override definition #ifndef STLPLUS_SIMPLE_PTR_REFCOUNT_ALLOC_OVERRIDE inline unsigned * simple_ptr_refcount_new() { return new unsigned (1); } inline void simple_ptr_refcount_delete( unsigned * pCount) { delete pCount; } #endif //////////////////////////////////////////////////////////////////////////////// } // end namespace stlplus #include "simple_ptr.tpp" #endif |