#include <iostream>
#include <type_traits>
using namespace std;
template<typename T> class IntrusivePtr;
template<typename TDerived>
class ReferenceCounting {
public:
typedef TDerived Derived ;
ReferenceCounting() : m_ref(0) { }
// Do not change reference count if an assignment has been done
ReferenceCounting& operator= (ReferenceCounting const&){ return *this; }
unsigned long int getRefCount() const{return m_ref;}
protected:
~ReferenceCounting() {std::cout <<"RC:DTOR" <<std::endl; }
private:
friend class IntrusivePtr<TDerived>;
friend class IntrusivePtr<const TDerived>;
unsigned long int addRef() const{
++m_ref;
std::cout << "RC::addRef: " << m_ref << std::endl;
return m_ref;
}
// NoDelete is for IntrusivePtr<T>().release()!
template<bool Delete = true>
void release() const{
--m_ref;
std::cout << "RC::release: " << m_ref << std::endl;
if(!m_ref && Delete){
std::cout << "RC::delete" << std::endl;
delete static_cast<Derived const *>(this);
}
}
mutable unsigned long int m_ref; // Mutable to be changeable also for const objects!
};
template<typename T>
class IntrusivePtr {
public:
using NonConstType = typename std::remove_const<T>::type;
IntrusivePtr() : m_p(nullptr) { }
// Explicit constructor from T* , because we want to avoid that this constructor can be used to convert implicitly to IntrusivePtr
// somewhere in the code which then deletes the resource unexpectetly!
// In this constructor/destructors we need a static_cast to really be sure if the type T inherits somehow from ReferenceCounting<T>
explicit IntrusivePtr(T* p) : m_p(p) {
if(p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
}
IntrusivePtr(const IntrusivePtr & rhs) : m_p(rhs.m_p) {
if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->addRef();
}
// Move support (temporaries)
// Copy construct from temporary
IntrusivePtr(IntrusivePtr && rhs) : m_p( rhs.m_p ){
rhs.m_p = 0; // temporary will not invoke reference count because pointer is zero!
}
~IntrusivePtr() {
if(m_p) static_cast<const ReferenceCounting<NonConstType> *>(m_p)->release();
}
// We want to assign the intrusive ptr to this class
// m_p points to A, rhs->m_p points to B
// This means, decrease ref count of current object A, an set m_p=rhs->m_p
// and increase ref count of rhs resource. This can by:
// Copy and swap idiom, call by value to copy the IntrusivePtr (ref count of B increments)
// swap this resource pointer into the local temporary rhs (only pointer swap)
// deleting the local rhs at end of function decrements ref count of initial resource A
IntrusivePtr& operator=(IntrusivePtr rhs) {
rhs.swap(*this); // swaps the resource pointers
return *this; // delete rhs-> which decrements correctly our initial resource A!
}
// Move Assignment (from temporary)
// Make sure rhs.m_p is zero and as a consequence the destruction of rhs does not invoke release!
IntrusivePtr & operator=(IntrusivePtr && rhs){
IntrusivePtr( std::move( rhs ) ).swap(*this);
return *this;
}
// Reset the IntrusivePtr to some other resource B,
// meaning decrementing our resource A and setting the new pointer to B
// and incrementing B
// Can also take a nullptr!, making it not default argument because avoiding syntax mistakes with release()
// which does a complete different thing (takes off the IntrusivePtr)
void reset(T* p) {
// Make temporary intrusive pointer for *p (incrementing ref count B)
// swapping pointers with our resource A, and deleting temporary, decrement A
IntrusivePtr(p).swap(*this);
}
// Release a IntrusivePtr from managing the shared resource
// Decrements ref count of this resource A but without deleting it!
T* release() {
static_cast<const ReferenceCounting<NonConstType> *>(m_p)->template release<false>();
T* p = m_p;
m_p = nullptr;
return p;
}
// Get the underlying pointer
T* get() const { return m_p; }
// Implicit cast to T*
operator T*() const { return m_p; }
// Implicit cast to T&
operator T&() const { return *m_p; }
T* operator->() const { return m_p; }
void swap(IntrusivePtr& rhs) {
std::swap(m_p, rhs.m_p);
}
private:
T* m_p;
};
// Helper to select the correct Derived type, the one which gets deleted in the ReferencCounting class
template<typename PossibleDerived,typename T>
struct Select{ typedef PossibleDerived type;};
template<typename T>
struct Select<void,T>{ typedef T type; };
// The base class!
template<typename PossibleDerived = void>
class A : public ReferenceCounting< typename Select<PossibleDerived, A<PossibleDerived> >::type >{
public:
A(){}
~A(){
std::cout << "A::DTOR: " <<this << std::endl;
}
int foo(){return i[10000];}
int i[10001];
};
// Use this class (because no brackets <> to write, more handy to write code)
class ANoTemplate final : public A<ANoTemplate>{
public:
ANoTemplate(){}
~ANoTemplate(){
std::cout << "B::DTOR: " <<this << std::endl;
}
};
// if we want to derive from this class we need to be very carefull, as
template<typename PossibleDerived = void>
class B : public A< typename Select<PossibleDerived, B<PossibleDerived> >::type >{
public:
~B(){
std::cout << "B::DTOR: " <<this << std::endl;
}
};
class AddendumToC{
public:
~AddendumToC(){
std::cout << "AddendumToC::DTOR " <<this << std::endl;
}
};
// Do not derive from this class, otherwise the refernce counting base class deletes the wrong class!
class C : public B<C>, public AddendumToC{
public:
~C(){
std::cout << "C::DTOR: " <<this << std::endl;
}
};
// DOOO NOT DOO INHERIT from C if you want reference counting with an intrusive pointer!
// In fact: If we have a IntrusivePtr onto a D instance, it will not compile as D cannot be cast into ReferenceCounting<C>!!)
class D : public C{};
int main(){
{
A<> *a = new A<>();
IntrusivePtr< A<> > p (a);
std::cout << "Created IntrusivePtr< A<> > " <<p<< std::endl;
}
{
ANoTemplate *a = new ANoTemplate();
IntrusivePtr< ANoTemplate > p (a);
std::cout << "Created Local IntrusivePtr< ANoTemplate > " <<p<< std::endl;
}
{
B<> *a = new B<>();
IntrusivePtr< B<> > p (a);
std::cout << "Created Local IntrusivePtr< B<> > " <<p<< std::endl;
}
{
C *a = new C();
IntrusivePtr< C > p (a);
std::cout << "Created Local IntrusivePtr< C > " <<p<< std::endl;
}
{
D *a = new D();
//IntrusivePtr< D > p (a);
//std::cout << "Created IntrusivePtr< D > " <<p<< std::endl;
//std::cout << "TAKE CARE: ONLY C gets deleted! " <<p<< std::endl;
}
{
using A=ANoTemplate;
A *a = new A();
IntrusivePtr<A> p (a);
std::cout << "Create Local " <<p<< std::endl;
{
std::cout << "Create IntrPtr" << std::endl;
IntrusivePtr<A> p2(p);
// Copy object
A c(p2);
std::cout << "Ref count of copied obj:" << c.getRefCount() << std::endl;
A b = *a;
std::cout << "Ref count of copied obj:" << b.getRefCount() << std::endl;
{
std::cout << "Create IntrPtr2 from;" << p.get()<< std::endl;
IntrusivePtr<A const> p3(p);
std::cout << "p3 points to " << p3.get() << " with ref count: " << p3->getRefCount()<< std::endl;
//p1->i[0]=10;
//p1.release(); p2.release(); // does not delete object
const A* r =p3.release();
std::cout << "Ref count of copied obj:" << r->getRefCount() << std::endl;
}
}
}
}