fork(1) download
  1. #include <iostream>
  2. #include <type_traits>
  3. using namespace std;
  4.  
  5. template<typename T> class IntrusivePtr;
  6.  
  7. template<typename TDerived>
  8. class ReferenceCounting {
  9. public:
  10. typedef TDerived Derived ;
  11.  
  12. ReferenceCounting() : m_ref(0) { }
  13.  
  14. // Do not change reference count if an assignment has been done
  15. ReferenceCounting& operator= (ReferenceCounting const&){ return *this; }
  16.  
  17. unsigned long int getRefCount() const{return m_ref;}
  18.  
  19. protected:
  20. ~ReferenceCounting() {std::cout <<"RC:DTOR" <<std::endl; }
  21.  
  22. private:
  23.  
  24. friend class IntrusivePtr<TDerived>;
  25. friend class IntrusivePtr<const TDerived>;
  26.  
  27. unsigned long int addRef() const{
  28. ++m_ref;
  29. std::cout << "RC::addRef: " << m_ref << std::endl;
  30. return m_ref;
  31. }
  32.  
  33. // NoDelete is for IntrusivePtr<T>().release()!
  34. template<bool Delete = true>
  35. void release() const{
  36. --m_ref;
  37. std::cout << "RC::release: " << m_ref << std::endl;
  38. if(!m_ref && Delete){
  39. std::cout << "RC::delete" << std::endl;
  40. delete static_cast<Derived const *>(this);
  41. }
  42. }
  43.  
  44. mutable unsigned long int m_ref; // Mutable to be changeable also for const objects!
  45. };
  46.  
  47.  
  48. template<typename T>
  49. class IntrusivePtr {
  50. public:
  51.  
  52. using NonConstType = typename std::remove_const<T>::type;
  53.  
  54. IntrusivePtr() : m_p(nullptr) { }
  55.  
  56. // Explicit constructor from T* , because we want to avoid that this constructor can be used to convert implicitly to IntrusivePtr
  57. // somewhere in the code which then deletes the resource unexpectetly!
  58. // In this constructor/destructors we need a static_cast to really be sure if the type T inherits somehow from ReferenceCounting<T>
  59. explicit IntrusivePtr(T* p) : m_p(p) {
  60. if(p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
  61. }
  62.  
  63. IntrusivePtr(const IntrusivePtr & rhs) : m_p(rhs.m_p) {
  64. if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
  65. }
  66.  
  67. // Move support (temporaries)
  68. // Copy construct from temporary
  69. IntrusivePtr(IntrusivePtr && rhs) : m_p( rhs.m_p ){
  70. rhs.m_p = 0; // temporary will not invoke reference count because pointer is zero!
  71. }
  72.  
  73. ~IntrusivePtr() {
  74. if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->release();
  75. }
  76.  
  77.  
  78. // We want to assign the intrusive ptr to this class
  79. // m_p points to A, rhs->m_p points to B
  80. // This means, decrease ref count of current object A, an set m_p=rhs->m_p
  81. // and increase ref count of rhs resource. This can by:
  82. // Copy and swap idiom, call by value to copy the IntrusivePtr (ref count of B increments)
  83. // swap this resource pointer into the local temporary rhs (only pointer swap)
  84. // deleting the local rhs at end of function decrements ref count of initial resource A
  85. IntrusivePtr& operator=(IntrusivePtr rhs) {
  86. rhs.swap(*this); // swaps the resource pointers
  87. return *this; // delete rhs-> which decrements correctly our initial resource A!
  88. }
  89.  
  90. // Move Assignment (from temporary)
  91. // Make sure rhs.m_p is zero and as a consequence the destruction of rhs does not invoke release!
  92. IntrusivePtr & operator=(IntrusivePtr && rhs){
  93. IntrusivePtr( std::move( rhs ) ).swap(*this);
  94. return *this;
  95. }
  96.  
  97. // Reset the IntrusivePtr to some other resource B,
  98. // meaning decrementing our resource A and setting the new pointer to B
  99. // and incrementing B
  100. // Can also take a nullptr!, making it not default argument because avoiding syntax mistakes with release()
  101. // which does a complete different thing (takes off the IntrusivePtr)
  102. void reset(T* p) {
  103. // Make temporary intrusive pointer for *p (incrementing ref count B)
  104. // swapping pointers with our resource A, and deleting temporary, decrement A
  105. IntrusivePtr(p).swap(*this);
  106. }
  107.  
  108. // Release a IntrusivePtr from managing the shared resource
  109. // Decrements ref count of this resource A but without deleting it!
  110. T* release() {
  111. static_cast<const ReferenceCounting<NonConstType> *>(m_p)->template release<false>();
  112. T* p = m_p;
  113. m_p = nullptr;
  114. return p;
  115. }
  116.  
  117. // Get the underlying pointer
  118. T* get() const { return m_p; }
  119.  
  120. // Implicit cast to T*
  121. operator T*() const { return m_p; }
  122. // Implicit cast to T&
  123. operator T&() const { return *m_p; }
  124.  
  125. T* operator->() const { return m_p; }
  126.  
  127. void swap(IntrusivePtr& rhs) {
  128. std::swap(m_p, rhs.m_p);
  129. }
  130. private:
  131. T* m_p;
  132. };
  133.  
  134.  
  135. // Helper to select the correct Derived type, the one which gets deleted in the ReferencCounting class
  136. template<typename PossibleDerived,typename T>
  137. struct Select{ typedef PossibleDerived type;};
  138.  
  139. template<typename T>
  140. struct Select<void,T>{ typedef T type; };
  141.  
  142.  
  143. // The base class!
  144. template<typename PossibleDerived = void>
  145. class A : public ReferenceCounting< typename Select<PossibleDerived, A<PossibleDerived> >::type >{
  146. public:
  147.  
  148. A(){}
  149. ~A(){
  150. std::cout << "A::DTOR: " <<this << std::endl;
  151. }
  152.  
  153. int foo(){return i[10000];}
  154.  
  155. int i[10001];
  156. };
  157.  
  158.  
  159. // Use this class (because no brackets <> to write, more handy to write code)
  160. class ANoTemplate final : public A<ANoTemplate>{
  161. public:
  162.  
  163. ANoTemplate(){}
  164. ~ANoTemplate(){
  165. std::cout << "B::DTOR: " <<this << std::endl;
  166. }
  167. };
  168.  
  169. // if we want to derive from this class we need to be very carefull, as
  170. template<typename PossibleDerived = void>
  171. class B : public A< typename Select<PossibleDerived, B<PossibleDerived> >::type >{
  172. public:
  173. using Base = A< typename Select<PossibleDerived, B<PossibleDerived> >::type >;
  174. ~B(){
  175. std::cout << "B::DTOR: " <<this << std::endl;
  176. }
  177.  
  178. };
  179.  
  180. class AddendumToC{
  181. public:
  182. ~AddendumToC(){
  183. std::cout << "AddendumToC::DTOR " <<this << std::endl;
  184. }
  185. };
  186.  
  187. // Do not derive from this class, otherwise the refernce counting base class deletes the wrong class!
  188. class C : public B<C>, public AddendumToC{
  189. public:
  190. ~C(){
  191. std::cout << "C::DTOR: " <<this << std::endl;
  192. }
  193. };
  194.  
  195. // DOOO NOT DOO INHERIT from C if you want reference counting with an intrusive pointer!
  196. // In fact: If we have a IntrusivePtr onto a D instance, it will not compile as D cannot be cast into ReferenceCounting<C>!!)
  197. class D : public C{};
  198.  
  199. int main(){
  200.  
  201.  
  202.  
  203. {
  204. A<> *a = new A<>();
  205. IntrusivePtr< A<> > p (a);
  206. std::cout << "Created IntrusivePtr< A<> > " <<p<< std::endl;
  207. }
  208.  
  209. {
  210. ANoTemplate *a = new ANoTemplate();
  211. IntrusivePtr< ANoTemplate > p (a);
  212. std::cout << "Created Local IntrusivePtr< ANoTemplate > " <<p<< std::endl;
  213. }
  214.  
  215. {
  216. IntrusivePtr< B<> > p (new B<>());
  217. std::cout << "Created Local IntrusivePtr< B<> > " <<p<< std::endl;
  218. }
  219.  
  220. {
  221. C *a = new C();
  222. IntrusivePtr< C > p (a);
  223. std::cout << "Created Local IntrusivePtr< C > " <<p<< std::endl;
  224. }
  225.  
  226. {
  227. D *a = new D();
  228. //IntrusivePtr< D > p (a);
  229. //std::cout << "Created IntrusivePtr< D > " <<p<< std::endl;
  230. //std::cout << "TAKE CARE: ONLY C gets deleted! " <<p<< std::endl;
  231. }
  232.  
  233. {
  234. IntrusivePtr< B<>::Base >(new B<>{});
  235. //std::cout << "Created Local IntrusivePtr< B::Base > " <<p<< " from wrong pointer to C class " << std::endl;
  236. }
  237.  
  238.  
  239.  
  240. {
  241. using A=ANoTemplate;
  242. A *a = new A();
  243. IntrusivePtr<A> p (a);
  244. std::cout << "Create Local " <<p<< std::endl;
  245. {
  246. std::cout << "Create IntrPtr" << std::endl;
  247. IntrusivePtr<A> p2(p);
  248. // Copy object
  249. A c(p2);
  250. std::cout << "Ref count of copied obj:" << c.getRefCount() << std::endl;
  251.  
  252. A b = *a;
  253. std::cout << "Ref count of copied obj:" << b.getRefCount() << std::endl;
  254. {
  255. std::cout << "Create IntrPtr2 from;" << p.get()<< std::endl;
  256. IntrusivePtr<A const> p3(p);
  257. std::cout << "p3 points to " << p3.get() << " with ref count: " << p3->getRefCount()<< std::endl;
  258. //p1->i[0]=10;
  259. //p1.release(); p2.release(); // does not delete object
  260. const A* r =p3.release();
  261. std::cout << "Ref count of copied obj:" << r->getRefCount() << std::endl;
  262. }
  263. }
  264. }
  265.  
  266. }
  267.  
Compilation error #stdin compilation error #stdout 0s 3240KB
stdin
Standard input is empty
compilation info
prog.cpp: In instantiation of 'IntrusivePtr<T>::IntrusivePtr(T*) [with T = A<B<> >]':
prog.cpp:234:41:   required from here
prog.cpp:60:19: error: invalid static_cast from type 'A<B<> >*' to type 'const ReferenceCounting<A<B<> > >*'
             if(p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
                   ^
prog.cpp: In instantiation of 'IntrusivePtr<T>::~IntrusivePtr() [with T = A<B<> >]':
prog.cpp:234:41:   required from here
prog.cpp:74:21: error: invalid static_cast from type 'A<B<> >*' to type 'const ReferenceCounting<A<B<> > >*'
             if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->release();
                     ^
stdout
Standard output is empty