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