Polymorphic SmartPtr

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Polymorphic SmartPtr

      I noticed that the code that accompanies the book states that the SmartPtr implementation used in the book's code is not polymorphic. That's a real drag since polymorphism IMHO is a trait that all SmartPtr implementations should support.

      Anyway here's my SmartPtr implementation. It is modelled on the SmartPtr implementation from the book. It supports polymorphism, intrusive and non-intrusive reference counting, and correctly handles const pointer types. One thing that it doesn't currently do is correctly delete the pointer if it is an array. However this can be easily fixed by adding a Deletion policy to the SmartPtr.

      One final note: the code uses member templates so you'll need at least VC 7 to compile the code.

      Brainfuck Source Code

      1. //------------------------------------------------------------------------
      2. // $Id: smartptr.h,v 1.10 2003/11/16 07:06:48 dpoon Exp $
      3. //
      4. // Copyright (c) 2003 David Poon
      5. //
      6. // Permission is hereby granted, free of charge, to any person
      7. // obtaining a copy of this software and associated documentation
      8. // files (the "Software"), to deal in the Software without
      9. // restriction, including without limitation the rights to use,
      10. // copy, modify, merge, publish, distribute, sublicense, and/or
      11. // sell copies of the Software, and to permit persons to whom
      12. // the Software is furnished to do so, subject to the following
      13. // conditions:
      14. //
      15. // The above copyright notice and this permission notice shall be
      16. // included in all copies or substantial portions of the Software.
      17. //
      18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      19. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
      20. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      21. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      22. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      23. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      25. // OTHER DEALINGS IN THE SOFTWARE.
      26. //------------------------------------------------------------------------
      27. #if !defined(SMARTPTR_H)
      28. #define SMARTPTR_H
      29. class RefCounted
      30. {
      31. public:
      32. RefCounted() : m_refCount(0)
      33. {
      34. }
      35. virtual ~RefCounted()
      36. {
      37. }
      38. int addRef()
      39. {
      40. return ++m_refCount;
      41. }
      42. int release()
      43. {
      44. int refCount = --m_refCount;
      45. if (refCount <= 0)
      46. delete this;
      47. return refCount;
      48. }
      49. int getRefCount() const
      50. {
      51. return m_refCount;
      52. }
      53. template <typename T>
      54. T *getPtr() const
      55. {
      56. return (T*)this;
      57. }
      58. private:
      59. int m_refCount;
      60. };
      61. //------------------------------------------------------------------------
      62. template <typename T>
      63. class RefCountedAdapter : public RefCounted
      64. {
      65. public:
      66. RefCountedAdapter(T *rhs) : m_pPointee(rhs)
      67. {
      68. }
      69. ~RefCountedAdapter()
      70. {
      71. if (getRefCount() <= 0)
      72. delete m_pPointee;
      73. }
      74. template <typename U>
      75. T *getPtr() const
      76. {
      77. return m_pPointee;
      78. }
      79. private:
      80. T *m_pPointee;
      81. };
      82. //------------------------------------------------------------------------
      83. class SmartPtrBase
      84. {
      85. public:
      86. SmartPtrBase() : m_pRefCounted(0)
      87. {
      88. }
      89. ~SmartPtrBase()
      90. {
      91. if (m_pRefCounted)
      92. m_pRefCounted->release();
      93. }
      94. RefCounted *getPtr() const
      95. {
      96. return m_pRefCounted;
      97. }
      98. protected:
      99. RefCounted *m_pRefCounted;
      100. };
      101. //------------------------------------------------------------------------
      102. template <typename T>
      103. class SmartPtr : public SmartPtrBase
      104. {
      105. public:
      106. SmartPtr()
      107. {
      108. }
      109. SmartPtr(SmartPtr<T> &rhs)
      110. {
      111. assign(rhs);
      112. }
      113. SmartPtr(RefCounted *rhs)
      114. {
      115. assign(rhs);
      116. }
      117. SmartPtr(SmartPtrBase &rhs)
      118. {
      119. assign(rhs);
      120. }
      121. SmartPtr(T *rhs)
      122. {
      123. assign(rhs);
      124. }
      125. SmartPtr &operator=(SmartPtr<T> &rhs)
      126. {
      127. if (!m_pRefCounted)
      128. {
      129. assign(rhs);
      130. }
      131. else
      132. {
      133. if (m_pRefCounted->getPtr<T>() != rhs.m_pRefCounted->getPtr<T>())
      134. assign(rhs);
      135. }
      136. return *this;
      137. }
      138. SmartPtr &operator=(RefCounted *rhs)
      139. {
      140. if (!m_pRefCounted)
      141. {
      142. assign(rhs);
      143. }
      144. else
      145. {
      146. if (m_pRefCounted->getPtr<T>() != rhs->getPtr<T>())
      147. assign(rhs);
      148. }
      149. return *this;
      150. }
      151. SmartPtr &operator=(SmartPtrBase &rhs)
      152. {
      153. if (!m_pRefCounted)
      154. {
      155. assign(rhs);
      156. }
      157. else
      158. {
      159. if (m_pRefCounted->getPtr<T>() != rhs.getPtr()->getPtr<T>())
      160. assign(rhs);
      161. }
      162. return *this;
      163. }
      164. SmartPtr &operator=(T *rhs)
      165. {
      166. if (!m_pRefCounted)
      167. {
      168. assign(rhs);
      169. }
      170. else
      171. {
      172. if (m_pRefCounted->getPtr<T>() != rhs)
      173. assign(rhs);
      174. }
      175. return *this;
      176. }
      177. template <typename U>
      178. operator SmartPtr<U>()
      179. {
      180. return SmartPtr<U>(m_pRefCounted);
      181. }
      182. ~SmartPtr()
      183. {
      184. }
      185. T *operator->() const
      186. {
      187. return m_pRefCounted->getPtr<T>();
      188. }
      189. T &operator*() const
      190. {
      191. return *m_pRefCounted->getPtr<T>();
      192. }
      193. bool operator!() const
      194. {
      195. return m_pRefCounted->getPtr<T>() == 0;
      196. }
      197. bool operator==(SmartPtrBase &rhs) const
      198. {
      199. return m_pRefCounted->getPtr<T>() == rhs.getPtr()->getPtr<T>();
      200. }
      201. bool operator!=(SmartPtrBase &rhs) const
      202. {
      203. return m_pRefCounted->getPtr<T>() != rhs.getPtr()->getPtr<T>();
      204. }
      205. private:
      206. void assign(SmartPtr<T> &rhs)
      207. {
      208. if (m_pRefCounted)
      209. m_pRefCounted->release();
      210. m_pRefCounted = rhs.m_pRefCounted;
      211. if (m_pRefCounted)
      212. m_pRefCounted->addRef();
      213. }
      214. void assign(RefCounted *rhs)
      215. {
      216. if (m_pRefCounted)
      217. m_pRefCounted->release();
      218. m_pRefCounted = rhs;
      219. if (m_pRefCounted)
      220. m_pRefCounted->addRef();
      221. }
      222. void assign(SmartPtrBase &rhs)
      223. {
      224. if (m_pRefCounted)
      225. m_pRefCounted->release();
      226. m_pRefCounted = rhs.getPtr();
      227. if (m_pRefCounted)
      228. m_pRefCounted->addRef();
      229. }
      230. void assign(T *rhs)
      231. {
      232. if (m_pRefCounted)
      233. m_pRefCounted->release();
      234. if (rhs != 0)
      235. {
      236. m_pRefCounted = new RefCountedAdapter<T>(rhs);
      237. m_pRefCounted->addRef();
      238. }
      239. }
      240. };
      241. #endif
      Display All


      Here's a little test application that should hopefully illustrate how to use my SmartPtr implementation. Notice that you shouldn't use RefCountedAdapter at all in your code - this class is used internally by the SmartPtr class for those pointer types that do not inherit from the RefCounted base class.

      C Source Code

      1. #if defined(_DEBUG)
      2. # define CRTDBG_MAP_ALLOC
      3. # include <crtdbg.h>
      4. #endif
      5. #include <iostream>
      6. #include "smartptr.h"
      7. class Base : public RefCounted
      8. {
      9. public:
      10. Base()
      11. {
      12. std::cout << "Base::Base()" << std::endl;
      13. }
      14. ~Base()
      15. {
      16. std::cout << "Base::~Base()" << std::endl;
      17. }
      18. void message() const
      19. {
      20. std::cout << "Base::message()" << std::endl;
      21. }
      22. };
      23. class Derived : public Base
      24. {
      25. public:
      26. Derived()
      27. {
      28. std::cout << "Derived::Derived()" << std::endl;
      29. }
      30. ~Derived()
      31. {
      32. std::cout << "Derived::~Derived()" << std::endl;
      33. }
      34. };
      35. void TestSmartPtr()
      36. {
      37. SmartPtr<Derived> pDerived;
      38. SmartPtr<const Base> pBase;
      39. pDerived = new Derived;
      40. pBase = pDerived;
      41. if (!pDerived)
      42. {
      43. std::cout << "SmartPtr<Derived> is NULL" << std::endl;
      44. }
      45. else
      46. {
      47. pDerived->message();
      48. (*pDerived).message();
      49. if (pDerived != pBase)
      50. std::cout << "SmartPtr<Derived> != SmartPtr<Base>" << std::endl;
      51. if (!(pDerived == pBase))
      52. std::cout << "SmartPtr<Derived> != SmartPtr<Base>" << std::endl;
      53. }
      54. }
      55. int main()
      56. {
      57. #if defined(_DEBUG)
      58. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF);
      59. #endif
      60. TestSmartPtr();
      61. std::cout << "Press enter to continue...";
      62. std::cin.get();
      63. return 0;
      64. }
      Display All

      The post was edited 2 times, last by dpoon ().

    • RE: Polymorphic SmartPtr

      Looks cool - by any chance did you try to replace the SmartPtr code given out with the source code???
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • RE: Polymorphic SmartPtr

      Hey dpoon,

      I get errors with the following:

      C Source Code

      1. #include <iostream>
      2. #include "smartptr.h"
      3. // BASE WEAPON CLASS
      4. class CWeapon_Data
      5. {
      6. public:
      7. // Constructor & Destructor
      8. CWeapon_Data()
      9. {
      10. std::cout<<"CWeapon_Data::CWeapon_Data()"<<std::endl;
      11. }
      12. virtual ~CWeapon_Data()
      13. {
      14. std::cout<<"CWeapon_Data::~CWeapon_Data()"<<std::endl;
      15. }
      16. // Methods
      17. virtual void vDamageMessage()
      18. {
      19. std::cout<<"CWeapon_Data::vDamageMessage()"<<std::endl;
      20. }
      21. // Members
      22. int m_iBaseDamage;
      23. int m_iWieldStr;
      24. int m_iWeight;
      25. bool m_bOneHanded;
      26. bool m_bWielded;
      27. };
      28. // DERIVED SWORD CLASS
      29. class CSword_Data : public CWeapon_Data
      30. {
      31. public:
      32. // Constructor & Destructor
      33. CSword_Data()
      34. {
      35. std::cout<<"CSword_Data::CSword_Data()"<<std::endl;
      36. }
      37. virtual ~CSword_Data()
      38. {
      39. std::cout<<"CSword_Data::~CSword_Data()"<<std::endl;
      40. }
      41. // Methods
      42. virtual void vDamageMessage()
      43. {
      44. std::cout<<"CSword_Data::vDamageMessage()"<<std::endl;
      45. }
      46. // Members
      47. };
      48. void TestSmartPtr()
      49. {
      50. SmartPtr<CSword_Data> BaseSword = new CSword_Data;
      51. SmartPtr<CWeapon_Data> BaseWeapon = new CWeapon_Data;
      52. BaseWeapon = BaseSword;
      53. if (!BaseSword)
      54. {
      55. std::cout<<"BaseSword<Derived> NULL"<<std::endl;
      56. }
      57. else
      58. {
      59. BaseSword->vDamageMessage();
      60. }
      61. }
      62. int main()
      63. {
      64. TestSmartPtr();
      65. std::cout << "Press enter to continue...";
      66. std::cin.get();
      67. return 0;
      68. }
      Display All


      An exception is thrown... I forget which one...

      Also, if instead I try and run the following:

      Source Code

      1. int main(void)
      2. {
      3. SmartPtr<CShortSword_Data> BaseShortSword = new CShortSword_Data;
      4. SmartPtr<CShortSword_Data> Base2 = new CShortSword_Data;
      5. Base2 = BaseShortSword; // Releases one
      6. SmartPtr<CSword_Data> BaseSword = BaseShortSword;
      7. SmartPtr<CWeapon_Data> BaseWeapon = BaseSword;
      8. Base2->vDamageMessage();
      9. BaseSword->vDamageMessage();
      10. BaseWeapon->vDamageMessage();
      11. return EXIT_SUCCESS;
      12. }
      Display All


      Here a Run-Time error is thrown as it can't equate one to the other, maybe all you need is a static_cast... not sure. Additionally, I noticed that in the above code when I do Base2 = BaseShortSword it does not Release Base2 as it should instead it makes a copy of it... this is very bad for memory management. One should be deleted and the other ptr rereferenced and AddRef() incremented.

      Nos

      The post was edited 1 time, last by Nostrademous ().

    • RE: Polymorphic SmartPtr

      Yeah I noticed those bugs as well :D
      I ran your code on a later version of the SmartPtr template class and I get this:

      Source Code

      1. CWeapon_Data::CWeapon_Data()
      2. CSword_Data::CSword_Data()
      3. CWeapon_Data::CWeapon_Data()
      4. CWeapon_Data::~CWeapon_Data()
      5. CSword_Data::vDamageMessage()
      6. CSword_Data::~CSword_Data()
      7. CWeapon_Data::~CWeapon_Data()
      8. Press enter to continue...


      Looking at the code you provided, this appears to be the expected behaviour.
      You didn't provide enough of the second code snippet for me to rerun that test.

      Anyway, here's an updated version of the SmartPtr template class.

      Brainfuck Source Code

      1. //------------------------------------------------------------------------
      2. // $Id: smartptr.h,v 1.11 2003/11/18 10:33:31 dpoon Exp $
      3. //
      4. // Copyright (c) 2003 David Poon
      5. //
      6. // Permission is hereby granted, free of charge, to any person
      7. // obtaining a copy of this software and associated documentation
      8. // files (the "Software"), to deal in the Software without
      9. // restriction, including without limitation the rights to use,
      10. // copy, modify, merge, publish, distribute, sublicense, and/or
      11. // sell copies of the Software, and to permit persons to whom
      12. // the Software is furnished to do so, subject to the following
      13. // conditions:
      14. //
      15. // The above copyright notice and this permission notice shall be
      16. // included in all copies or substantial portions of the Software.
      17. //
      18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      19. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
      20. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      21. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      22. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      23. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      25. // OTHER DEALINGS IN THE SOFTWARE.
      26. //------------------------------------------------------------------------
      27. #if !defined(SMARTPTR_H)
      28. #define SMARTPTR_H
      29. template <typename T>
      30. class RefCounted
      31. {
      32. public:
      33. RefCounted(T *pPointee = 0) : m_refCount(0), m_pPointee(pPointee)
      34. {
      35. }
      36. ~RefCounted()
      37. {
      38. if (getRefCount() > 0)
      39. release();
      40. }
      41. void addRef()
      42. {
      43. ++m_refCount;
      44. }
      45. void release()
      46. {
      47. if (--m_refCount <= 0)
      48. {
      49. delete m_pPointee;
      50. m_pPointee = 0;
      51. delete this;
      52. }
      53. }
      54. int getRefCount() const
      55. {
      56. return m_refCount;
      57. }
      58. T *getPointee() const
      59. {
      60. return m_pPointee;
      61. }
      62. private:
      63. int m_refCount;
      64. T *m_pPointee;
      65. };
      66. //------------------------------------------------------------------------
      67. class SmartPtrBase
      68. {
      69. public:
      70. SmartPtrBase() : m_pRefCounted(0)
      71. {
      72. }
      73. ~SmartPtrBase()
      74. {
      75. }
      76. bool isNull() const
      77. {
      78. return m_pRefCounted == 0;
      79. }
      80. template <typename T>
      81. RefCounted<T> *getRefCounted() const
      82. {
      83. return reinterpret_cast<RefCounted<T>*>(m_pRefCounted);
      84. }
      85. protected:
      86. template <typename T>
      87. void setRefCounted(RefCounted<T> *pRefCounted)
      88. {
      89. m_pRefCounted = pRefCounted;
      90. }
      91. private:
      92. void *m_pRefCounted;
      93. };
      94. //------------------------------------------------------------------------
      95. template <typename T>
      96. class SmartPtr : public SmartPtrBase
      97. {
      98. public:
      99. SmartPtr()
      100. {
      101. }
      102. SmartPtr(SmartPtr<T> &rhs)
      103. {
      104. assign(rhs);
      105. }
      106. SmartPtr(SmartPtrBase &rhs)
      107. {
      108. assign(rhs);
      109. }
      110. SmartPtr(T *rhs)
      111. {
      112. assign(rhs);
      113. }
      114. SmartPtr &operator=(SmartPtr<T> &rhs)
      115. {
      116. if (isNull())
      117. {
      118. assign(rhs);
      119. }
      120. else
      121. {
      122. if (getRefCounted<T>()->getPointee() != rhs.getRefCounted<T>()->getPointee())
      123. assign(rhs);
      124. }
      125. return *this;
      126. }
      127. SmartPtr &operator=(SmartPtrBase &rhs)
      128. {
      129. if (isNull())
      130. {
      131. assign(rhs);
      132. }
      133. else
      134. {
      135. if (getRefCounted<T>()->getPointee() != rhs.getRefCounted<T>()->getPointee())
      136. assign(rhs);
      137. }
      138. return *this;
      139. }
      140. SmartPtr &operator=(T *rhs)
      141. {
      142. if (isNull())
      143. {
      144. assign(rhs);
      145. }
      146. else
      147. {
      148. if (getRefCounted<T>()->getPointee() != rhs)
      149. assign(rhs);
      150. }
      151. return *this;
      152. }
      153. template <typename U>
      154. operator SmartPtr<U>()
      155. {
      156. return SmartPtr<U>(getRefCounted<T>());
      157. }
      158. ~SmartPtr()
      159. {
      160. if (!isNull())
      161. getRefCounted<T>()->release();
      162. }
      163. T *operator->() const
      164. {
      165. return getRefCounted<T>()->getPointee();
      166. }
      167. T &operator*() const
      168. {
      169. return *getRefCounted<T>()->getPointee();
      170. }
      171. bool operator!() const
      172. {
      173. if (isNull())
      174. return true;
      175. return getRefCounted<T>()->getPointee() == 0;
      176. }
      177. bool operator==(SmartPtrBase &rhs) const
      178. {
      179. return getRefCounted<T>()->getPointee() == rhs.getRefCounted<T>()->getPointee();
      180. }
      181. bool operator!=(SmartPtrBase &rhs) const
      182. {
      183. return getRefCounted<T>()->getPointee() != rhs.getRefCounted<T>()->getPointee();
      184. }
      185. private:
      186. void assign(SmartPtr<T> &rhs)
      187. {
      188. if (!isNull())
      189. getRefCounted<T>()->release();
      190. setRefCounted(rhs.getRefCounted<T>());
      191. if (!isNull())
      192. getRefCounted<T>()->addRef();
      193. }
      194. void assign(SmartPtrBase &rhs)
      195. {
      196. if (!isNull())
      197. getRefCounted<T>()->release();
      198. setRefCounted<T>(rhs.getRefCounted<T>());
      199. if (!isNull())
      200. getRefCounted<T>()->addRef();
      201. }
      202. void assign(T *rhs)
      203. {
      204. if (!isNull())
      205. getRefCounted<T>()->release();
      206. if (rhs != 0)
      207. {
      208. setRefCounted<T>(new RefCounted<T>(rhs));
      209. getRefCounted<T>()->addRef();
      210. }
      211. }
      212. };
      213. #endif
      Display All


      Note that in this version I have cleaned up the design so that the only class you should be using is the SmartPtr template class. The other two classes are used to implement SmartPtr.

      The post was edited 1 time, last by dpoon ().

    • Just a quick aside - the 2nd edition source code uses Boost's shared_ptr template, which is polymorphic and quite easy to use.
      Mr.Mike
      Author, Programmer, Brewer, Patriot