smart.hpp

Go to the documentation of this file.
00001 //////////////////////////////////////////////////////////////////////////
00002 //    This material is provided "as is", with absolutely no warranty
00003 //  expressed or implied. Any use is at your own risk.
00004 //
00005 //    Permission to use or copy this software for any purpose is hereby
00006 //  granted without fee, provided the above notices are retained on all
00007 //  copies. Permission to modify the code and to distribute modified code
00008 //  is granted, provided the above notices are retained, and a notice that
00009 //  the code was modified is included with the above copyright notice.
00010 //////////////////////////////////////////////////////////////////////////
00011 /** @file
00012 @brief Smart pointers.
00013 
00014     This header file contains interface and implementation
00015   of the smart pointer with reference counting.
00016 
00017 @author Sergey Polichnoy
00018 */
00019 #ifndef __OMNI_SMART_HPP_
00020 #define __OMNI_SMART_HPP_
00021 
00022 #include <omni/defs.hpp>
00023 
00024 #include <assert.h>
00025 
00026 namespace omni
00027 {
00028   namespace smart
00029   {
00030 
00031 //////////////////////////////////////////////////////////////////////////
00032 /// @brief Reference counting.
00033 /**
00034     This class implements reference counting and automatic
00035   self destruction if there is no more references.
00036 
00037     The objects of any derived class should be created using @b new
00038   operator. These objects must be destroyed using detach() method.
00039 
00040     It is recommended to use virtual derivation for derived classes.
00041 
00042     This class is used with SharedPtr template.
00043 
00044 @code
00045   class MyObj: public virtual SharedObj {
00046     // ...
00047   };
00048 @endcode
00049 
00050 @see @ref omni_smart_shared
00051 */
00052 class SharedObj: private omni::NonCopyable {
00053 protected:
00054   SharedObj();
00055   virtual ~SharedObj();
00056 
00057 public:
00058   long N_refs() const;
00059 
00060 public:
00061   void attach();
00062   bool detach();
00063 
00064 private:
00065   long m_N_refs;
00066 };
00067 
00068     // forward declarations
00069     namespace details
00070     {
00071       template<typename T>
00072         class SharedPtr_Base;
00073     }
00074 
00075 
00076 //////////////////////////////////////////////////////////////////////////
00077 /// @brief Smart pointer with reference counting.
00078 /**
00079     This class automatically manages object's number of references.
00080   Constructor increases the number of references. Destructor decreases
00081   number of references. The object should be dynamically created
00082   using @b new operator.
00083 
00084     This class behaves the same as a simple pointer to the object.
00085   It define dereference operators and compare operators (== и !=).
00086   The smart pointer can be "null".
00087 
00088     The type @a T can be derived from SharedObj class. In this case the
00089   SharedPtr class has no additional costs. Otherwise for each object
00090   the reference counter will be dynamically created.
00091 
00092 @param T The object type.
00093 @see @ref omni_smart_shared
00094 */
00095 template<typename T>
00096 class SharedPtr: private details::SharedPtr_Base<T>::base_type {
00097   typedef typename details::SharedPtr_Base<T>::base_type inherited;
00098   typedef SharedPtr<T> ThisType;
00099 
00100 public:
00101   typedef typename inherited::reference reference;  ///< @brief Reference type.
00102   typedef typename inherited::pointer   pointer;    ///< @brief Pointer type.
00103 
00104 public:
00105 
00106 //////////////////////////////////////////////////////////////////////////
00107 /// @brief Create "null" pointer.
00108 /**
00109     This constructor creates "null" pointer.
00110 
00111 @code
00112   SharedPtr<MyClass> p;
00113   f(*p); // null pointer exception
00114 @endcode
00115 */
00116   SharedPtr()
00117     : inherited(0)
00118   {}
00119 
00120 
00121 //////////////////////////////////////////////////////////////////////////
00122 /// @brief Attach simple pointer.
00123 /**
00124     This constructor increases (@a *p) object's number of references.
00125 
00126 @param[in] ptr Simple pointer.
00127 */
00128   explicit SharedPtr(pointer ptr)
00129     : inherited(ptr)
00130   {
00131     attach();
00132   }
00133 
00134 
00135 //////////////////////////////////////////////////////////////////////////
00136 /// @brief Copy construction.
00137 /**
00138     This constructor increases (@a *x) object's number of references.
00139 
00140 @param[in] other Smart pointer.
00141 */
00142   SharedPtr(const ThisType &other)
00143     : inherited(other)
00144   {
00145     attach();
00146   }
00147 
00148 
00149 //////////////////////////////////////////////////////////////////////////
00150 /// @brief Destruction.
00151 /**
00152     The destructor decreases object's number of references.
00153   So if there is no more references, object will be destroyed.
00154 */
00155   ~SharedPtr()
00156   {
00157     detach();
00158   }
00159 
00160 
00161 //////////////////////////////////////////////////////////////////////////
00162 /// @brief Assign smart pointer.
00163 /**
00164     Если автоматический указатель ссылался на объект,
00165   то у старого объекта уменьшается количество ссылок.
00166 
00167     Запоминает указатель на новый объект &*x, и если он не является нулевым,
00168   то увеличивает у нового объекта количество ссылок.
00169 
00170 @param[in] x Smart pointer.
00171 @return Self reference.
00172 */
00173   ThisType& operator=(const ThisType &other)
00174   {
00175     ThisType t(other);
00176     swap(t);
00177 
00178     return *this;
00179   }
00180 
00181 
00182 public:
00183 
00184 //////////////////////////////////////////////////////////////////////////
00185 /// @brief Ссылка на объект.
00186 /**
00187     Метод разъименовывает умный указатель.
00188   Если указатель нулевой, произойдет обращение по нулевому адресу.
00189 
00190 @return Ссылка на объект.
00191 */
00192   reference operator*() const
00193   {
00194     const pointer ptr = get();
00195     return *ptr;
00196   }
00197 
00198 
00199 //////////////////////////////////////////////////////////////////////////
00200 /// @brief Указатель на объект.
00201 /**
00202     Метод разъименовывает умный указатель.
00203 
00204 @return Простой указатель на объект.
00205 */
00206   pointer operator->() const
00207   {
00208     return get();
00209   }
00210 
00211 public:
00212 
00213 //////////////////////////////////////////////////////////////////////////
00214 /// @brief Проверить на "нулевой" указатель.
00215 /**
00216     Метод проверяет является ли умный указатель нулевым.
00217 
00218 @return @b true Если умный указатель нулевой, иначе @b false.
00219 */
00220   pointer get() const
00221   {
00222     return inherited::get();
00223   }
00224 
00225 
00226 //////////////////////////////////////////////////////////////////////////
00227 /// @brief Проверить уникальность указателя.
00228 /**
00229     Метод проверяет, является ли текущий автоматический указатель
00230   уникальным, т.е. на хранимый объект существует только одна ссылка.
00231 
00232 @return @b true Если умный указатель уникальный, иначе @b false.
00233 */
00234   bool unique() const
00235   {
00236     return (get() != 0)
00237       && (N_refs() == 1);
00238   }
00239 
00240 public:
00241   /// @brief Unspecified bool type.
00242   typedef pointer (ThisType::*unspecified_bool_type)() const;
00243 
00244   /// @brief Check for non "NULL".
00245   operator unspecified_bool_type() const // never throws
00246   {
00247     return (get() == 0) ? 0 : &ThisType::get;
00248   }
00249 
00250   /// @brief Check for "NULL".
00251   bool operator!() const
00252   {
00253     return get() == 0;
00254   }
00255 
00256   /// @brief Swap two pointers
00257   void swap(ThisType &other)
00258   {
00259     inherited::swap(other);
00260   }
00261 };
00262 
00263 
00264 
00265 //////////////////////////////////////////////////////////////////////////
00266 /// @brief Проверить два указателя на равенство.
00267 /**
00268     Оператор проверяет два умных укзателя на равенство.
00269   Указатели считаются равными, если они указывают
00270   на один и тот же объект или оба являются нулевыми.
00271 
00272 @param[in] x Первый умный указатель.
00273 @param[in] y Второй умный указатель.
00274 @return @b true Если указатели равны, иначе @b false.
00275 */
00276 template<typename T> inline
00277   bool operator==(const SharedPtr<T> &x, const SharedPtr<T> &y)
00278 {
00279   return x.get() == y.get();
00280 }
00281 
00282 
00283 //////////////////////////////////////////////////////////////////////////
00284 /// @brief Проверить два указателя на равенство.
00285 /**
00286     Оператор проверяет умный и простой укзатели на равенство.
00287   Указатели считаются равными, если они указывают
00288   на один и тот же объект или оба являются нулевыми.
00289 
00290 @param[in] x Умный указатель.
00291 @param[in] y Простой указатель.
00292 @return @b true Если указатели равны, иначе @b false.
00293 */
00294 template<typename T> inline
00295   bool operator==(const SharedPtr<T> &x, const T *y)
00296 {
00297   return x.get() == y;
00298 }
00299 
00300 
00301 //////////////////////////////////////////////////////////////////////////
00302 /// @brief Проверить два указателя на равенство.
00303 /**
00304     Оператор проверяет простой и умный укзатели на равенство.
00305   Указатели считаются равными, если они указывают
00306   на один и тот же объект или оба являются нулевыми.
00307 
00308 @param[in] x Простой указатель.
00309 @param[in] y Умный указатель.
00310 @return @b true Если указатели равны, иначе @b false.
00311 */
00312 template<typename T> inline
00313   bool operator==(const T *x, const SharedPtr<T> &y)
00314 {
00315   return x == y.get();
00316 }
00317 
00318 
00319 //////////////////////////////////////////////////////////////////////////
00320 /// @brief Проверить два указателя на неравенство.
00321 /**
00322     Оператор проверяет два умных укзателя на неравенство.
00323 
00324 @param[in] x Первый умный указатель.
00325 @param[in] y Второй умный указатель.
00326 @return @b true Если указатели не равны, иначе @b false.
00327 */
00328 template<typename T> inline
00329   bool operator!=(const SharedPtr<T> &x, const SharedPtr<T> &y)
00330 {
00331   return x.get() != y.get();
00332 }
00333 
00334 
00335 //////////////////////////////////////////////////////////////////////////
00336 /// @brief Проверить два указателя на неравенство.
00337 /**
00338     Оператор проверяет умный и простой укзатели на неравенство.
00339 
00340 @param[in] x Умный указатель.
00341 @param[in] y Простой указатель.
00342 @return @b true Если указатели не равны, иначе @b false.
00343 */
00344 template<typename T> inline
00345   bool operator!=(const SharedPtr<T> &x, const T *y)
00346 {
00347   return x.get() != y;
00348 }
00349 
00350 
00351 //////////////////////////////////////////////////////////////////////////
00352 /// @brief Проверить два указателя на неравенство.
00353 /**
00354     Оператор проверяет простой и умный укзатели на неравенство.
00355 
00356 @param[in] x Простой указатель.
00357 @param[in] y Умный указатель.
00358 @return @b true Если указатели не равны, иначе @b false.
00359 */
00360 template<typename T> inline
00361   bool operator!=(const T *x, const SharedPtr<T> &y)
00362 {
00363   return x != y.get();
00364 }
00365 
00366 
00367 //////////////////////////////////////////////////////////////////////////
00368 /// @brief Swap two smart pointers.
00369 template<typename T> inline
00370   void swap(SharedPtr<T> &x, SharedPtr<T> &y)
00371 {
00372   x.swap(y);
00373 }
00374 
00375     // implementation...
00376     namespace details
00377     {
00378 
00379 //////////////////////////////////////////////////////////////////////////
00380 // @brief Базовый класс без накладных расходов.
00381 template<typename T>
00382 class SharedPtr_ThinBase: private omni::NonCopyable {
00383   typedef SharedPtr_ThinBase<T> ThisType;
00384 
00385 protected:
00386   typedef T& reference;
00387   typedef T* pointer;
00388 
00389 protected: // constructors...
00390   explicit SharedPtr_ThinBase(pointer ptr)
00391     : m_ptr(ptr)
00392   {}
00393   SharedPtr_ThinBase(const ThisType &other)
00394     : m_ptr(other.m_ptr)
00395   {}
00396 
00397 protected: // properties
00398   pointer get() const
00399   {
00400     return m_ptr;
00401   }
00402   long N_refs() const
00403   {
00404     return m_ptr->SharedObj::N_refs();
00405   }
00406 
00407 protected: // reference counting
00408   void attach()
00409   {
00410     if (m_ptr)
00411       m_ptr->SharedObj::attach();
00412   }
00413   void detach()
00414   {
00415     if (m_ptr)
00416       m_ptr->SharedObj::detach();
00417   }
00418 
00419 protected: // auxiliary
00420   void swap(ThisType &other)
00421   {
00422     pointer ptr = m_ptr;
00423     m_ptr = other.m_ptr;
00424     other.m_ptr = ptr;
00425   }
00426 
00427 private:
00428   pointer m_ptr;
00429 };
00430 
00431 
00432 //////////////////////////////////////////////////////////////////////////
00433 // @brief Базовый класс с накладными расходами.
00434 template<typename T>
00435 class SharedPtr_FatBase: private omni::NonCopyable {
00436   typedef SharedPtr_FatBase<T> ThisType;
00437 
00438 protected:
00439   typedef T& reference;
00440   typedef T* pointer;
00441 
00442 protected: // constructors
00443   explicit SharedPtr_FatBase(pointer ptr)
00444     : m_ptr(ptr), m_N(0)
00445   {}
00446   SharedPtr_FatBase(const ThisType &other)
00447     : m_ptr(other.m_ptr), m_N(other.m_N)
00448   {}
00449 
00450 protected: // properties
00451   pointer get() const
00452   {
00453     return m_ptr;
00454   }
00455   long N_refs() const
00456   {
00457     return m_N->N_refs();
00458   }
00459 
00460 protected: // reference counting
00461   void attach()
00462   {
00463     if (m_ptr && !m_N)
00464       m_N = new Counter();
00465 
00466     if (m_N)
00467       m_N->attach();
00468   }
00469   void detach()
00470   {
00471     if (m_N)
00472     {
00473       if (m_N->detach())
00474         delete m_ptr;
00475     }
00476   }
00477 
00478 protected: // auxiliary
00479 
00480   void swap(ThisType &other)
00481   {
00482     pointer ptr = m_ptr;
00483     m_ptr = other.m_ptr;
00484     other.m_ptr = ptr;
00485 
00486     Counter *n = m_N;
00487     m_N = other.m_N;
00488     other.m_N = n;
00489   }
00490 
00491 private:
00492   // TODO: pool::FastObj to base class?
00493   class Counter: public SharedObj {};
00494 
00495 private:
00496   pointer m_ptr;
00497   Counter *m_N;
00498 };
00499 
00500 // SharedPtr_ThinBase selector
00501 template<bool IsThin>
00502 struct SharedPtr_BaseSelector {
00503   template<typename T>
00504   struct Base {
00505     typedef SharedPtr_ThinBase<T> base_type;
00506   };
00507 };
00508 
00509 // SharedPtr_FatBase selector
00510 template<>
00511 struct SharedPtr_BaseSelector<false> {
00512   template<typename T>
00513   struct Base {
00514     typedef SharedPtr_FatBase<T> base_type;
00515   };
00516 };
00517 
00518 
00519 // @brief Выбор базового класса в зависимости от типа T.
00520 //
00521 // если тип T производный от SharedObj, используется SharedPtr_ThinBase
00522 // иначе, используется SharedPtr_FatBase
00523 template<typename T>
00524 class SharedPtr_Base {
00525 private:
00526   struct YES { char dummy[666]; };
00527   struct  NO { char dummy[999]; };
00528 
00529   static YES check(const SharedObj*);
00530   static  NO check(...);
00531 
00532   enum { is_thin = sizeof(YES) == sizeof(check((const T*)0)) };
00533 
00534 public:
00535   typedef typename SharedPtr_BaseSelector<is_thin>
00536     ::template Base<T>::base_type base_type;
00537 };
00538 
00539     } // implementation
00540   } // smart namespace
00541 } // omni namespace
00542 
00543 #endif // __OMNI_SMART_HPP_

Generated on Wed Jun 6 17:27:46 2007 for OMNI by  doxygen 1.5.2