New shared pointer
Koos Vriezen
koos.vriezen at xs4all.nl
Fri Sep 16 10:52:24 BST 2005
On Sat, Sep 10, 2005 at 02:15:20PM +0200, Koos Vriezen wrote:
> On Saturday 10 September 2005 Frerich Raabe wrote:
>
> > the current shared pointer implementation in KShared/KSharedPtr is a little
> > too simplistic IMHO:
> >
> > - it requires all classes with onto whose objects you want to point with a
> > KSharedPtr to provide the interface given by KShared; in practice, this means
> > that you might have to change the code of the target class because you either
> > need to make it inherit KShared, or you add the ref()/deref() functions
>
> [..]
>
> > P.S.: I considered using QSharedData/QSharedDataPointer but that duo has
> > exactly the same two problems as KShared/KSharedPtr I mentioned above. I also
> > considered boost::shared_ptr but noticed that it pulls in a lot of other
> > funky template magic (which, as the honest tears of a certain SUSE engineer
> > agree with, might be a little too much for the compiler of choice).
>
> FWIW, and probably too late, there are at leat two 'light' version of
> the boost onces. There is one in the remote desktop code and mine in
> KMPlayer, which is even lighter. It would have been nice if you would
> have taken one of these, as they are already well tested and it's a bit
> of a waste to write yet another one, no?
Sorry for not attaching one, here is an alternative that has a
- SharedPtr<T>
- WeakPtr<T>
- Item<T>
classes. SharedPtr<T> and WeakPtr<T> can be used for the same shared
data and, as such, provides a QGuardedPtr functionality.
Item<T> is needed as base class for classed that _do_ need 'this' to
pass to other objects that _do_ want to track its lifetime or prevent it
from destruction.
I didn't merge in the things that were added in the proposed version.
For instance, in this version to empty a SharedPtr, one would simply
assign '0' to it. Also the volatile keyword can be added in the
SharedData class.
> One thing you are missing is the addition of boost's weak_ptr, which I
> use alot. Same reason you mention above for having to inherit KShared
> for having shared pointers is having to inherit QObject for QGuardedPtr.
> So with weak_ptr one has both.
>
> To give an example where WeakPtr has a huge benefit in KMPlayer.
> KMPlayer has a w3c like DOM tree based on shared_ptr/weak_ptr. If a
> certain node wants to create child nodes, and pass itself as parent, it
> can't just send 'this' because 'this' is also in a SharedPtr of its
> parent. So what I did was that all nodes have a WeakPtr of itself as
> protected member.
> This is the disadvantige of SharedPtr, a certain object has no 'this'
> anymore that it can pass arround.
>
> Koos
-------------- next part --------------
/* This file is part of the KDE project
*
* Copyright (C) 2004 Koos Vriezen <koos.vriezen at xs4all.nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* until boost gets common, a more or less compatable one ..
*/
#ifndef _SHAREDPTR_H_
#define _SHAREDPTR_H_
//#define SHAREDPTR_DEBUG
#ifdef SHAREDPTR_DEBUG
extern int shared_data_count;
#include <iostream>
#endif
/**
* Shared data for SharedPtr and WeakPtr objects.
**/
template <class T>
struct SharedData {
SharedData (T * t, bool w) : use_count (w?0:1), weak_count (1), ptr (t) {
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::SharedData use:" << use_count << " weak:" << weak_count << " total:" << ++shared_data_count << std::endl;
#endif
}
#ifdef SHAREDPTR_DEBUG
~SharedData () { std::cerr << "SharedData::~SharedData" << " total:" << --shared_data_count << std::endl; }
#endif
void addRef ();
void addWeakRef ();
void release ();
void releaseWeak ();
void dispose ();
int use_count;
int weak_count;
T * ptr;
};
template <class T> inline void SharedData<T>::addRef () {
use_count++;
weak_count++;
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::addRef use:" << use_count << " weak:" << weak_count << std::endl;
#endif
}
template <class T> inline void SharedData<T>::addWeakRef () {
weak_count++;
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::addWeakRef use:" << use_count << " weak:" << weak_count << std::endl;
#endif
}
template <class T> inline void SharedData<T>::releaseWeak () {
ASSERT (weak_count > 0 && weak_count > use_count);
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::releaseWeak use:" << use_count << " weak:" << weak_count-1 << std::endl;
#endif
if (--weak_count <= 0) delete this;
}
template <class T> inline void SharedData<T>::release () {
ASSERT (use_count > 0);
if (--use_count <= 0) dispose ();
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::release use:" << use_count << " weak:" << weak_count << std::endl;
#endif
releaseWeak ();
}
template <class T> inline void SharedData<T>::dispose () {
ASSERT (use_count == 0);
#ifdef SHAREDPTR_DEBUG
std::cerr << "SharedData::dispose use:" << use_count << " weak:" << weak_count << std::endl;
#endif
delete ptr;
ptr = 0;
}
template <class T> struct WeakPtr;
/**
* Shared class based on boost shared
* This makes it possible to share pointers w/o having to worry about
* memory leaks. A pointer gets deleted as soon as the last Shared pointer
* gets destroyed. As such, never use (or be extremely carefull) not to
* use pointers or references to shared objects
**/
template <class T>
struct SharedPtr {
SharedPtr () : data (0L) {};
explicit SharedPtr (T *t) : data (t ? new SharedData<T> (t, false) : 0L) {}
SharedPtr (const SharedPtr<T> & s) : data (s.data) { if (data) data->addRef (); }
SharedPtr (const WeakPtr <T> &);
~SharedPtr () { if (data) data->release (); }
SharedPtr<T> & operator = (const SharedPtr<T> &);
SharedPtr<T> & operator = (const WeakPtr<T> &);
SharedPtr<T> & operator = (T *);
T * ptr () const { return data ? data->ptr : 0L; }
T * operator -> () { return data ? data->ptr : 0L; }
T * operator -> () const { return data ? data->ptr : 0L; }
T & operator * () { return *data->ptr; }
const T & operator * () const { return *data->ptr; }
// operator bool () const { return data && data->ptr; }
bool operator == (const SharedPtr<T> & s) const { return data == s.data; }
bool operator == (const WeakPtr<T> & w) const;
bool operator == (const T * t) const { return (!t && ! data) || (data && data->ptr == t); }
bool operator != (const SharedPtr<T> & s) const { return data != s.data; }
bool operator != (const WeakPtr<T> & w) const;
bool operator != (const T * t) const { return !operator == (t); }
operator T * () { return data ? data->ptr : 0L; }
operator T * () const { return data ? data->ptr : 0L; }
mutable SharedData<T> * data;
};
template <class T>
inline SharedPtr<T> & SharedPtr<T>::operator = (const SharedPtr<T> & s) {
if (data != s.data) {
SharedData<T> * tmp = data;
data = s.data;
if (data) data->addRef ();
if (tmp) tmp->release ();
}
return *this;
}
template <class T> inline SharedPtr<T> & SharedPtr<T>::operator = (T * t) {
if ((!data && t) || (data && data->ptr != t)) {
if (data) data->release ();
data = t ? new SharedData<T> (t, false) : 0L;
}
return *this;
}
/**
* Weak version of SharedPtr. This will also have access to the SharedData
* pointer, only these object wont prevent destruction of the shared
* pointer, hence weak references
*/
template <class T>
struct WeakPtr {
WeakPtr () : data (0L) {};
explicit WeakPtr (T * t) : data (t ? new SharedData<T> (t, true) : 0) {}
WeakPtr (const WeakPtr<T> & s) : data (s.data) { if (data) data->addWeakRef (); }
WeakPtr (const SharedPtr<T> & s) : data (s.data) { if (data) data->addWeakRef (); }
~WeakPtr () { if (data) data->releaseWeak (); }
WeakPtr<T> & operator = (const WeakPtr<T> &);
WeakPtr<T> & operator = (const SharedPtr<T> &);
WeakPtr<T> & operator = (T *);
T * ptr () const { return data ? data->ptr : 0L; }
T * operator -> () { return data ? data->ptr : 0L; }
const T * operator -> () const { return data ? data->ptr : 0L; }
T & operator * () { return *data->ptr; }
const T & operator * () const { return *data->ptr; }
// operator bool () const { return data && !!data->ptr; }
bool operator == (const WeakPtr<T> & w) const { return data == w.data; }
bool operator == (const SharedPtr<T> & s) const { return data == s.data; }
bool operator == (const T * t) const { return (!t && !data) || (data && data.ptr == t); }
bool operator != (const WeakPtr<T> & w) const { return data != w.data; }
bool operator != (const SharedPtr<T> & s) const { return data != s.data; }
operator T * () { return data ? data->ptr : 0L; }
operator const T * () const { return data ? data->ptr : 0L; }
mutable SharedData<T> * data;
};
/**
* Base class for objects that will be used as SharedPtr/WeakPtr pointers
* and need a handle to the shared data wherein it resides.
* Item<T> keeps its own copy of the shared SharedData<T> as a weak refence.
* A typical usage would look like:
*
* class Foo : public Item<Foo> {
* ..
* };
* typedef Item<Foo>::SharedType FooPtr;
* FooPtr foo = (new Foo)->self();
*
* \sa: self()
*/
template <class T>
class Item {
public:
typedef SharedPtr <T> SharedType;
typedef WeakPtr <T> WeakType;
virtual ~Item () {}
SharedType self () const { return m_self; }
protected:
Item ();
WeakType m_self;
private:
Item (const Item <T> &); // forbidden copy constructor
};
template <class T>
inline WeakPtr<T> & WeakPtr<T>::operator = (const WeakPtr<T> & w) {
if (data != w.data) {
SharedData<T> * tmp = data;
data = w.data;
if (data) data->addWeakRef ();
if (tmp) tmp->releaseWeak ();
}
return *this;
}
template <class T>
inline WeakPtr<T> & WeakPtr<T>::operator = (const SharedPtr<T> & s) {
if (data != s.data) {
SharedData<T> * tmp = data;
data = s.data;
if (data) data->addWeakRef ();
if (tmp) tmp->releaseWeak ();
}
return *this;
}
template <class T>
inline WeakPtr<T> & WeakPtr<T>::operator = (T * t) {
if (data) data->releaseWeak ();
data = t ? new SharedData<T> (t, true) : 0L;
return *this;
}
template <class T> inline SharedPtr<T>::SharedPtr (const WeakPtr <T> & w) : data (w.data) {
if (data) data->addRef ();
}
template <class T>
inline SharedPtr<T> & SharedPtr<T>::operator = (const WeakPtr<T> & s) {
if (data != s.data) {
SharedData<T> * tmp = data;
data = s.data;
if (data) data->addRef ();
if (tmp) tmp->release ();
}
return *this;
}
template <class T>
inline bool SharedPtr<T>::operator == (const WeakPtr<T> & w) const {
return data == w.data;
}
template <class T>
inline bool SharedPtr<T>::operator != (const WeakPtr<T> & w) const {
return data != w.data;
}
template <class T>
inline Item<T>::Item () : m_self (static_cast <T*> (this)) {}
#endif
More information about the kde-core-devel
mailing list