New shared pointer
Ignacio Castaño
castanyo at yahoo.es
Fri Sep 9 22:42:06 BST 2005
I think that the kind of shared pointer that you propose has a couple of
problems. It adds an additional indirection and a memory allocation for the
intermediate shared object. It's also error prone. Imagine that you have an
object and its owner references it through your shared pointer. Then you pass
a regular pointer to the shared object to the exiting API. Then the object is
retrieved by some other object that uses a shared pointer to reference it
too, but now you have two shared objects pointing to the same one. When the
first reference is destroyed, the object goes away, leaving a dangling
pointer. boost::shared_ptr has exactly the same problems.
The delete problem of your example, can be easily avoided by not providing a
cast operator. I usually also declare the constructors that take regular
pointers as explicit, to make the use of the shared pointers more explicit
and less error prone.
Having an embedded reference count in the object being shared is useful for
debugging purpoused. It's also possible to add additional functionallity to
the shared objects through the base class. You can for example keep track of
an object and its references by using a linked list and that can be done
transparently only when a debuging is enabled, at no cost in release mode. It
also provides a way of implementing weak pointers that for some applications
can be very useful.
Hope that helps,
--
Ignacio Castaño
castanyo at yahoo.es
Frerich Raabe <raabe at kde.org> escribió:
> Moin,
>
> 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
>
> - it provides an implicit conversion to T* which is very evil. For
> instance,
> this works:
>
> KSharedPtr<Widget> p( new Foo );
> KSharedPtr<Widget> q = p;
> delete p;
> q->doStuff(); // boom!
>
> This is especially likely to happen when you're porting code to KSharedPtr
> because you might forget to remove a delete call. I noticed that somebody
> added my name to the corresponding entry in kdelibs/TODO which points these
>
> things out so here's a try at a slightly less simplistic (yet by no means
> comprehensive) shoot at a shared pointer. It's very much like the old
> KSharedPtr, except that
>
> - you don't have to inherit KShared anymore, you can use it with any type.
> - it doesn't have an implicit conversion to T* so you cannot accidentally
> delete the internal object. You can still use the get() function to
> retrieve
> the raw pointer if you really need to.
> - the data() function had a weird name - get() is not much better I guess,
> but
> a few other shared pointer implementations use that name, so I went for it.
>
> Feel free to suggest something more appropriate.
>
> Note that this new KSharedPtr is not like QSharedDataPointer because it
> does
> not do copy-on-write (it doesn't automatically detach() when using the
> non-const * and -> operators) - it features a copy() function though, so
> you
> can have explicitely shared objects.
>
> Please have a look and point stuff out which is worth being pointed out.
> It's
> still kinda rough (I only tested it with a few code snippets), but you'd
> get
> the idea what's supposed to happen. I'm very well aware that this is a
> great
> bikeshed topic but please refrain from endless theoretical discussions. I'd
>
> appreciate proper C++ code which breaks when using this class or straight
> "Looks okay, we can give it a try and see what breaks", tho.
>
> - Frerich
>
> 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).
> > /*
> * This file is part of the KDE libraries.
> *
> * Copyright 2005 Frerich Raabe <raabe at kde.org>
> *
> * Redistribution and use in source and binary forms, with or without
> * modification, are permitted provided that the following conditions
> * are met:
> *
> * 1. Redistributions of source code must retain the above copyright
> * notice, this list of conditions and the following disclaimer.
> * 2. Redistributions in binary form must reproduce the above copyright
> * notice, this list of conditions and the following disclaimer in the
> * documentation and/or other materials provided with the distribution.
> *
> * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
> * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> WARRANTIES
> * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
> * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> USE,
> * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> */
> #ifndef KSHAREDPTR_H
> #define KSHAREDPTR_H
>
> #ifdef KDE3_SUPPORT
> #include "kdelibs_export.h"
> struct KDECORE_EXPORT KShared
> {
> virtual ~KShared() {}
> };
> #endif
>
> template <class T>
> class KSharedPtr;
>
> template <class T>
> bool operator==( const KSharedPtr<T> &lhs, const KSharedPtr<T> &rhs )
> {
> return lhs.d == rhs.d;
> }
>
> template <class T>
> bool operator!=( const KSharedPtr<T> &lhs, const KSharedPtr<T> &rhs )
> {
> return !operator==( lhs, rhs );
> }
>
> /**
> * A reference-counting pointer.
> *
> * This class serves as a replacement for ordinary C++ pointers, allowing
> * you to handle objects as if they are explicitely shared, handling
> * reference counting behind the scenes.
> *
> * An explicitely shared object provides very cheap copying since only a
> * pointer is copied and a reference count is updated. When an explicitely
> * shared object goes out of scope, the internal reference count gets
> * decreased. The data which multiple explicitely shared objects share is
> * only deleted when the last object which references it goes out of scope.
> *
> * Explicitely shared objects do not provide copy-on-write semantics; this
> * means that changing one object will also change all other objects which
> * share the data. Consider this:
> * \code
> * KSharedPtr<QString> w = new QString( "Hello" );
> * KSharedPtr<QString> v = w; // Now both v and w say 'Hello'
> * v->clear(); // Clears both v and w!
> * \endcode
> * The last line of code will clear both v and w, since they share the same
> * QString internally. To acquire a copy of the contained object which is
> * independant of all other copies, use the copy() function, as in:
> * \code
> * KSharedPtr<QString> w = new QString( "Hello" );
> * KSharedPtr<QString> v = w.copy(); // Make v a detached copy of w
> * v->clear(); // v is cleared, w still says 'Hello'
> * \endcode
> *
> * @author Frerich Raabe <raabe at kde.org>
> * @short A reference counting pointer.
> */
> template <class T>
> class KSharedPtr
> {
> friend bool operator==<>( const KSharedPtr<T> &lhs, const KSharedPtr<T>
> &rhs );
> friend bool operator!=<>( const KSharedPtr<T> &lhs, const KSharedPtr<T>
> &rhs );
> public:
> KSharedPtr( T *obj )
> {
> d = new KSharedData;
> d->ref = 1;
> d->obj = obj;
> }
>
> KSharedPtr( const KSharedPtr<T> &other )
> {
> d = new KSharedData;
> *this = other;
> }
>
> ~KSharedPtr()
> {
> deref();
> }
>
> KSharedPtr<T> &operator=( T *rhs )
> {
> deref();
> if ( !d ) {
> d = new KSharedData;
> }
> d->obj = rhs;
> d->ref = 1;
> return *this;
> }
>
> KSharedPtr<T> &operator=( const KSharedPtr<T> &rhs )
> {
> ++rhs.d->ref;
> deref();
> d = rhs.d;
> return *this;
> }
>
> const T &operator*() const { return *d->obj; }
> T &operator*() { return *d->obj; }
> const T *operator->() const { return d->obj; }
> T *operator->() { return d->obj; }
> operator bool() const { return d->obj != 0; }
>
> KSharedPtr<T> copy()
> {
> KSharedPtr<T> tmp = *this;
> deref();
> return tmp;
> }
>
> T *get()
> {
> return d->obj;
> }
>
> #ifdef KDE3_SUPPORT
> T *data()
> {
> return get();
> }
> #endif
>
> private:
> struct KSharedData
> {
> int ref;
> T *obj;
> };
>
> void deref()
> {
> if ( --d->ref -= 0 ) {
> delete d->obj;
> delete d;
> d = 0;
> }
> }
>
> KSharedData *d;
> };
>
> #endif // KSHAREDPTR_H
>
______________________________________________
Renovamos el Correo Yahoo!
Nuevos servicios, más seguridad
http://correo.yahoo.es
More information about the kde-core-devel
mailing list