I was reading into std::shared_ptr and I got to thinking...how does it keep up with a reference count?
I was reading into std::shared_ptr and I got to thinking...how does it keep up with a reference count?
With a counter!
You set the counter to 1 when you first allocate the memory.
After that copy construction/assignment copies the counter along with the data and increments it.
The destructor is interesting.
You decrement your counter and if it reaches zero, you deallocate the data.
Yes with a counter but if each smart pointer has its own counter, then they will all have different values and it wouldn't help you at all.
All shared pointers that point to the same resource share the counter.
When you first create a shared_ptr, you create a counter on the heap.
When you copy a shared_ptr, you copy that pointer to the counter to the new smart pointers and increase the ref count.
When a smart_ptr is destroyed, you decrease the ref counter.
When all pointers to the resource are destroyed, the free the counter and the resource.
This makes sense. It gets harder to account for when you consider you need a way to increment or decrement the reference count only for pointers sharing the same value. You could probably do this with a static map (having just a single variable to do the counting seems impossible).
WndProc = (2[b] || !(2[b])) ? SufferNobly : TakeArms;
Still you would still need some structure to memorize that a certain pointer value is associated with a certain reference counter (pointer) value, right?
Like for instance if I have two shared pointers of the same template type, that are pointing to two different objects of the same type. This would mean I need two reference counters, right? When I go to copy one of the shared pointers to object A, how do I know which reference counter to give to the copy?
(Sorry if I'm being frustrating lol, I really am trying to understand ).
Edit: I'm an idiot lol, I see what you are saying, the object being copied has it's own reference counter. Forgive me I got stuck on this "static variable" idea for some reason.
Last edited by Alpo; 05-09-2015 at 09:18 PM.
WndProc = (2[b] || !(2[b])) ? SufferNobly : TakeArms;
So is this kinda what you are saying?
Code:#ifndef __POINTER_H #define __POINTER_H #include <iostream> template <class T> class Pointer { public: /* Constructors/Destructor */ Pointer(); Pointer(T *ptr); Pointer(const Pointer<T>& ptr); ~Pointer(); /* Modifiers */ void reset(); /* Accessors */ T* raw(); int count(); T& operator [] (const size_t& index); T& operator * (); T& operator -> (); T** operator & (); /* Assignment Operators */ Pointer<T>& operator = (T *ptr); Pointer<T>& operator = (const Pointer<T>& ptr); /* Arithmetic Assignment Operators */ Pointer<T>& operator ++ (); Pointer<T> operator ++ (int); Pointer<T>& operator -- (); Pointer<T> operator -- (int); template <class U> Pointer<T>& operator += (const U& other); template <class U> Pointer<T>& operator -= (const U& other); template <class U> Pointer<T>& operator *= (const U& other); template <class U> Pointer<T>& operator /= (const U& other); template <class U> Pointer<T>& operator %= (const U& other); template <class U> Pointer<T>& operator >>= (const U& other); template <class U> Pointer<T>& operator <<= (const U& other); template <class U> Pointer<T>& operator &= (const U& other); template <class U> Pointer<T>& operator |= (const U& other); template <class U> Pointer<T>& operator ^= (const U& other); /* Relational Operators */ template <class U> friend bool operator == (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator != (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator >= (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator <= (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator > (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator < (const Pointer<T>& ptr, const U& other); /* Conversions */ bool operator ! (); template <class U> operator U(); template <class U> operator U*(); template <class U> operator Pointer<U>(); /* Std Overloads */ friend std::ostream& operator << (std::ostream& os, const Pointer<T>& ptr); protected: T *rawPointer; int *refCounter; }; template <class T> Pointer<T>::Pointer() { refCounter = new int; *refCounter = 0; rawPointer = nullptr; } template <class T> Pointer<T>::Pointer(T *ptr) { refCounter = new int; *refCounter = 1; rawPointer = ptr; } template <class T> Pointer<T>::Pointer(const Pointer<T>& ptr) { if (*ptr.refCounter) refCounter = ++*ptr.refCounter; else refCounter = nullptr; rawPointer = ptr.rawPointer; } template <class T> Pointer<T>::~Pointer() { if (--*refCounter == 0) delete[] rawPointer; } template <class T> void Pointer<T>::reset() { if (--*refCounter == 0) delete[] rawPointer; rawPointer = nullptr; } template <class T> T* Pointer<T>::raw() { return rawPointer; } template <class T> int count() { return *refCounter; } template <class T> T& Pointer<T>::operator [] (const size_t& index) { return rawPointer[index]; } template <class T> T& Pointer<T>::operator * () { return *rawPointer; } template <class T> T& Pointer<T>::operator -> () { return *rawPointer } template <class T> T** Pointer<T>::operator & () { return &rawPointer; } template <class T> Pointer<T>& Pointer<T>::operator = (T *ptr) { if (rawPointer != ptr) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer = ptr; } } template <class T> Pointer<T>& Pointer<T>::operator = (const Pointer<T>& ptr) { if (rawPointer != ptr.rawPointer) { if (--*refCounter == 0) delete[] rawPointer; refCounter = ++*ptr.refCounter; rawPointer = ptr.rawPointer; } } template <class T> Pointer<T>& Pointer<T>::operator ++ () { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; ++rawPointer; } template <class T> Pointer<T> Pointer<T>::operator ++ (int) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; ++rawPointer; } template <class T> Pointer<T>& Pointer<T>::operator -- () { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; --rawPointer; } template <class T> Pointer<T> Pointer<T>::operator -- (int) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; --rawPointer; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator += (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer += other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator -= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer -= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator *= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer *= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator /= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer /= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator %= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer %= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator >>= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer >>= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator <<= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer <<= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator &= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer &= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator |= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer |= other; } template <class T> template <class U> Pointer<T>& Pointer<T>::operator ^= (const U& other) { if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer ^= other; } template <class T> template <class U> bool Pointer<T>::operator == (const Pointer<T>& ptr, const U& other) { return (rawPointer == other); } template <class T> template <class U> bool Pointer<T>::operator != (const Pointer<T>& ptr, const U& other) { return (rawPointer != other); } template <class T> template <class U> bool Pointer<T>::operator >= (const Pointer<T>& ptr, const U& other) { return (rawPointer >= other); } template <class T> template <class U> bool Pointer<T>::operator <= (const Pointer<T>& ptr, const U& other) { return (rawPointer <= other); } template <class T> template <class U> bool Pointer<T>::operator > (const Pointer<T>& ptr, const U& other) { return (rawPointer > other); } template <class T> template <class U> bool Pointer<T>::operator < (const Pointer<T>& ptr, const U& other) { return (rawPointer < other); } template <class T> bool Pointer<T>::operator ! () { return !rawPointer; } template <class T> template <class U> Pointer<T>::operator U() { return rawPointer; } template <class T> template <class U> Pointer<T>::operator U*() { return rawPointer; } template <class T> template <class U> Pointer<T>::operator Pointer<U>() { Pointer<U> ptr; ptr.refCounter = refCounter; ptr.rawPointer = rawPointer; } template <class T> std::ostream& operator << (std::ostream& os, const Pointer<T>& ptr) { return (os << rawPointer); } #endif // __POINTER_H
these 2 functions will cause memory leak:
you never delete counter (actually in any function..).Code:template <class T> Pointer<T>::Pointer() { refCounter = new int; *refCounter = 0; rawPointer = nullptr; } template <class T> Pointer<T>::~Pointer() { if (--*refCounter == 0) delete[] rawPointer; }
(I also don't get the whole mathematical operators += , -= etc. if they reflect pointer arithmatics - your implementation is not.. )
Last edited by Dave11; 05-09-2015 at 11:52 PM.
Kind of, yes, though there are some bugs in there.
Then there's all the operators involved which makes it a little more complicated. Like, do you want to allow assigning raw pointers? You could theoretically pull one from another smart pointer and add assign it to a different one. That way, you could two difference reference counts, which I believe, is why the standard prohibits this kind of thing.
Also, operator < is usually overloaded to do the comparison *p < value to make smart pointers play nice with containers.
O_oSo is this kinda what you are saying?
You have just so many copy/paste bugs.
Stop talking nonsense.Also, operator < is usually overloaded to do the comparison *p < value to make smart pointers play nice with containers.
Virtually all smart pointers overload the comparison operators as to forward comparison to the underlying values of the owned pointers not the values at which the pointers point.
Soma
“Salem Was Wrong!” -- Pedant Necromancer
“Four isn't random!” -- Gibbering Mouther
I put it together really quick. I overloaded all of the assignment stuff because actual pointers let you do that. As for + - and such, the conversion operator takes care of those. I'm looking at my code and I'm baffled, when do I need to delete the reference counter?
EDIT: I think I found where...
BTW delete[] refCounter should be delete refCounter
Code:#ifndef __POINTER_H #define __POINTER_H #include <iostream> template <class T> class Pointer { public: /* Constructors/Destructor */ Pointer(); Pointer(T *ptr); Pointer(const Pointer<T>& ptr); ~Pointer(); /* Modifiers */ void reset(); /* Accessors */ T* raw(); int count(); T& operator [] (const size_t& index); T& operator * (); T& operator -> (); T** operator & (); const T* raw() const; const int count() const; const T& operator [] (const size_t& index) const; const T& operator * () const; const T& operator -> () const; const T** operator & () const; /* Assignment Operators */ Pointer<T>& operator = (T *ptr); Pointer<T>& operator = (const Pointer<T>& ptr); /* Arithmetic Assignment Operators */ Pointer<T>& operator ++ (); Pointer<T> operator ++ (int); Pointer<T>& operator -- (); Pointer<T> operator -- (int); template <class U> Pointer<T>& operator += (const U& other); template <class U> Pointer<T>& operator -= (const U& other); template <class U> Pointer<T>& operator *= (const U& other); template <class U> Pointer<T>& operator /= (const U& other); template <class U> Pointer<T>& operator %= (const U& other); template <class U> Pointer<T>& operator >>= (const U& other); template <class U> Pointer<T>& operator <<= (const U& other); template <class U> Pointer<T>& operator &= (const U& other); template <class U> Pointer<T>& operator |= (const U& other); template <class U> Pointer<T>& operator ^= (const U& other); /* Relational Operators */ template <class U> friend bool operator == (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator != (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator >= (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator <= (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator > (const Pointer<T>& ptr, const U& other); template <class U> friend bool operator < (const Pointer<T>& ptr, const U& other); /* Conversions */ bool operator ! (); template <class U> operator U(); template <class U> operator U*(); template <class U> operator Pointer<U>(); /* Std Overloads */ friend std::ostream& operator << (std::ostream& os, const Pointer<T>& ptr); protected: T *rawPointer; int *refCounter; }; template <class T> inline Pointer<T>::Pointer() { refCounter = new int; *refCounter = 0; rawPointer = nullptr; } template <class T> inline Pointer<T>::Pointer(T *ptr) { refCounter = new int; *refCounter = 1; rawPointer = ptr; } template <class T> inline Pointer<T>::Pointer(const Pointer<T>& ptr) { if (*ptr.refCounter) refCounter = ++*ptr.refCounter; else refCounter = nullptr; rawPointer = ptr.rawPointer; } template <class T> inline Pointer<T>::~Pointer() { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; } template <class T> inline void Pointer<T>::reset() { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 0; rawPointer = nullptr; } template <class T> inline T* Pointer<T>::raw() { return rawPointer; } template <class T> inline int Pointer<T>::count() { return *refCounter; } template <class T> inline T& Pointer<T>::operator [] (const size_t& index) { return rawPointer[index]; } template <class T> inline T& Pointer<T>::operator * () { return *rawPointer; } template <class T> inline T& Pointer<T>::operator -> () { return *rawPointer } template <class T> inline T** Pointer<T>::operator & () { return &rawPointer; } template <class T> inline const T* Pointer<T>::raw() const { return rawPointer; } template <class T> inline const int Pointer<T>::count() const { return *refCounter; } template <class T> inline const T& Pointer<T>::operator [] (const size_t& index) const { return rawPointer[index]; } template <class T> inline const T& Pointer<T>::operator * () const { return *rawPointer; } template <class T> inline const T& Pointer<T>::operator -> () const { return *rawPointer } template <class T> inline const T** Pointer<T>::operator & () const { return &rawPointer; } template <class T> inline Pointer<T>& Pointer<T>::operator = (T *ptr) { if (rawPointer != ptr) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer = ptr; } return *this; } template <class T> inline Pointer<T>& Pointer<T>::operator = (const Pointer<T>& ptr) { if (rawPointer != ptr) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; *refCounter = ++*ptr.refCounter; rawPointer = ptr.rawPointer; } return *this; } template <class T> inline Pointer<T>& Pointer<T>::operator ++ () { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; ++rawPointer; return *this; } template <class T> inline Pointer<T> Pointer<T>::operator ++ (int) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; ++rawPointer; return Array<T>(*this); } template <class T> inline Pointer<T>& Pointer<T>::operator -- () { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; --rawPointer; return *this; } template <class T> inline Pointer<T> Pointer<T>::operator -- (int) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; --rawPointer; return Array<T>(*this); } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator += (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer += other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator -= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer -= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator *= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer *= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator /= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer /= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator %= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer %= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator >>= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer >>= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator <<= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer <<= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator &= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer &= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator |= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer |= other; return *this; } template <class T> template <class U> inline Pointer<T>& Pointer<T>::operator ^= (const U& other) { if (*refCounter == 0) delete[] refCounter; else if (--*refCounter == 0) delete[] rawPointer; refCounter = new int; *refCounter = 1; rawPointer ^= other; return *this; } template <class T, class U> inline bool operator == (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer == other); } template <class T, class U> inline bool operator != (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer != other); } template <class T, class U> inline bool operator >= (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer >= other); } template <class T, class U> inline bool operator <= (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer <= other); } template <class T, class U> inline bool operator > (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer > other); } template <class T, class U> inline bool operator < (const Pointer<T>& ptr, const U& other) { return (ptr.rawPointer < other); } template <class T> inline bool Pointer<T>::operator ! () { return !rawPointer; } template <class T> template <class U> inline Pointer<T>::operator U() { return rawPointer; } template <class T> template <class U> inline Pointer<T>::operator U*() { return rawPointer; } template <class T> template <class U> inline Pointer<T>::operator Pointer<U>() { Pointer<U> ptr; ptr.refCounter = refCounter; ptr.rawPointer = rawPointer; return ptr; } template <class T> inline std::ostream& operator << (std::ostream& os, const Pointer<T>& ptr) { return (os << rawPointer); } #endif // __POINTER_H
Last edited by cmajor28; 05-10-2015 at 10:12 AM.
O_oI overloaded all of the assignment stuff because actual pointers let you do that.
No. You've overloaded far too many operators.
You should take a look at what operations are valid on native pointers.
No. You actually do need to write the companion operators.As for + - and such, the conversion operator takes care of those.
Your code is problematic in numerous ways, and the majority of your problems come from not using idiomatic techniques.I'm looking at my code and I'm baffled, when do I need to delete the reference counter?
The counter may be released when the actual object is released, but you could keep the reference count around as a debugging aid in some tools.
You haven't.I think I found where...
A destructor is only called once unless you have some extraordinary bugs.
Your code besides still has a lot of bugs.
[Edit]
If you toss the extraneous code, I'd be inclined to point out specific problems.
As your code is, you have too many mistakes for me to explain them all.
You should start small; you can implement the default constructor, value (the one taking a pointer to type from template argument) constructor, copy constructor, destructor, and a `get' method.
[/Edit]
Soma
Last edited by phantomotap; 05-10-2015 at 10:37 AM.
“Salem Was Wrong!” -- Pedant Necromancer
“Four isn't random!” -- Gibbering Mouther
If I remember correctly the only operators a pointer really needs are dereference (* and -> for user defined types), equality (==), and inequality (!=). Dereference might be fairly obvious, but equality and inequality compare the contained address to another address. Like, mysmartptr<T> and another mysmartptr<T>, or mysmartptr<T> and T*. Also you probably need operator = for memory management purposes.
The other operators, as phantomotap mentioned, do not need to be implemented, because you probably mean to use the contained type's operators instead. That being the case, you will invariably write something like *p + *q, or whatever it is. Smart pointers also don't normally double as iterators, where you have other operators like [], ++ or --.
Last edited by whiteflags; 05-10-2015 at 11:09 AM.