You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/krdc/smartptr.h

434 lines
12 KiB

/***************************************************************************
begin : Wed Jan 1 17:56 CET 2003
copyright : (C) 2003 by Tim Jansen
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef SMARTPTR_H
#define SMARTPTR_H
#include <tqstring.h>
class WeakPtr;
/**
* @internal
*/
struct SmartPtrRefCount {
SmartPtrRefCount(int toObj, int toThis) :
refsToObject(toObj),
refsToThis(toThis) {
}
int refsToObject; // number of pointers to the object, 0 if released
int refsToThis; // number of pointer to the ref count
};
/**
* SmartPtr is a reference counting smart pointer. When you create
* the first instance it will create a new counter for the pointee
* and it share it with all other SmartPtr instances for that pointee.
* The reference count can only be kept accurate when you do not create
* a second 'realm' of references by converting a SmartPtr into a
* regular pointer and then create a new SmartPtr from that pointer.
* When the last instance of a SmartPtr for the given object has been
* deleted the object itself will be deleted. You can stop the SmartPtr
* system to manage an object by calling @ref release() on any of
* the pointers pointing to that object. All SmartPtrs will then stop
* managing the object, and you can also safely create a second 'realm'.
*
* SmartPtr can be combined with @ref WeakPtr. A WeakPtr
* does not influence its life cycle, but notices when a SmartPtr
* deletes the object.
*
* The recommended way to use SmartPtr and @ref WeakPtr is to use SmartPtr
* for all aggregations and WeakPtr for associations. Unlike auto_ptr,
* SmartPtr can be used in collections.
*
* SmartPtr is not thread-safe. All instances of SmartPtrs pointing
* to a pointee must always be in the same thread, unless you break
* the 'realm' by calling @ref release() in one thread and give the
* original pointer the other thread. It can then create a new SmartPtr
* and control the lifecycle of the object.
* @see WeakPtr
*/
template <class T>
class SmartPtr
{
public: // members are public because of problems with gcc 3.2
friend class WeakPtr;
/// @internal
T* ptr;
/// @internal
mutable SmartPtrRefCount *rc; // if !rc, refcount=1 is assumed
protected:
void freePtr() {
if (!ptr)
return;
if (!rc)
delete ptr;
else {
if (rc->refsToObject > 0) {
Q_ASSERT(rc->refsToObject >= rc->refsToThis);
if (rc->refsToObject == 1) {
delete ptr;
rc->refsToObject = -1;
}
else
rc->refsToObject--;
}
rc->refsToThis--;
if (rc->refsToThis < 1)
delete rc;
}
}
void init(T *sptr, SmartPtrRefCount *&orc)
{
ptr = sptr;
if (!sptr)
rc = 0;
else if (!orc) {
orc = new SmartPtrRefCount(2, 2);
rc = orc;
}
else {
rc = orc;
rc->refsToThis++;
if (rc->refsToObject) {
// prevent initialization from invalid WeakPtr
Q_ASSERT(rc->refsToObject > 0);
rc->refsToObject++;
}
}
}
SmartPtr(T *p, SmartPtrRefCount *&orc)
{
init(p, orc);
}
public:
/**
* Creates a SmartPtr that refers to the given pointer @p.
* SmartPtr will take control over the object and delete it
* when the last SmartPtr that referes to the object
* has been deleted.
* @param p the pointer to the object to manage, or the null pointer
*/
SmartPtr(T* p = 0) :
ptr(p),
rc(0)
{
}
/**
* Copies the given SmartPtr, sharing ownership with the other
* pointer. Increases the reference count by 1 (if the object
* has not been @ref release()d).
* @param sptr the object pointer to copy
*/
SmartPtr(const SmartPtr<T> &sptr)
{
init(sptr.ptr, sptr.rc);
}
/**
* Copies the given SmartPtr, sharing ownership with the other
* pointer. Increases the reference count by 1 (if the object
* has not been @ref release()d).
* @param sptr the object pointer to copy
*/
template<class T2>
SmartPtr(const SmartPtr<T2> &sptr)
{
init((T*)sptr.ptr, sptr.rc);
}
/**
* Delete the pointer and, if the reference count is one and the object has not
* been released, deletes the object.
*/
~SmartPtr() {
freePtr();
}
/**
* Copies the given SmartPtr, sharing ownership with the other
* pointer. Increases the reference count by 1 (if the object
* has not been @ref release()d). The original object will be dereferenced
* and thus deleted, if the reference count is 1.
* @param sptr the object pointer to copy
* @return this SmartPtr object
*/
SmartPtr &operator=(const SmartPtr<T> &sptr) {
if (this == &sptr)
return *this;
freePtr();
init(sptr.ptr, sptr.rc);
return *this;
}
/**
* Copies the given SmartPtr, sharing ownership with the other
* pointer. Increases the reference count by 1 (if the object
* has not been @ref release()d). The original object will be dereferenced
* and thus deleted, if the reference count is 1.
* @param sptr the object pointer to copy
* @return this SmartPtr object
*/
template<class T2>
SmartPtr &operator=(const SmartPtr<T2> &sptr) {
if (this == static_cast<SmartPtr<T> >(&sptr))
return *this;
freePtr();
init(static_cast<T>(sptr.ptr), sptr.rc);
return *this;
}
/**
* Sets the SmartPointer to the given value. The original object
* will be dereferenced and thus deleted, if the reference count is 1.
* @param p the value of the new pointer
*/
void set(T *p) {
if (ptr == p)
return;
freePtr();
ptr = p;
rc = 0;
}
/**
* Releases the ptr. This means it will not be memory-managed
* anymore, neither by this SmartPtr nor by any other pointer that
* shares the object. The caller is responsible for freeing the
* object. It is possible to assign the plain pointer (but not the
* SmartPtr!) to another SmartPtr that will then start memory
* management. This may be useful, for example, to let another
* thread manage the lifecyle.
* @return the pointer, must be freed by the user
* @see data()
*/
T* release() {
if (!rc)
rc = new SmartPtrRefCount(0, 1);
else
rc->refsToObject = 0;
return ptr;
}
/**
* Sets the SmartPointer to the given value. The original object
* will be dereferenced and thus deleted, if the reference count is 1.
* @param p the value of the new pointer
* @return this SmartPtr object
*/
SmartPtr &operator=(T *p) {
set(p);
return *this;
}
/**
* Returns true if the SmartPtr points to an actual object, false
* if it is the null pointer.
* @return true for an actual pointer, false for the null pointer
*/
operator bool() const {
return ptr != 0;
}
/**
* Returns the plain pointer to the pointed object. The object will
* still be managed by the SmartPtr. You must ensure that the pointer
* is valid (so don't delete the SmartPtr before you are done with the
* plain pointer).
* @return the plain pointer
* @see data()
* @see release()
* @see WeakPtr
*/
template<class T2>
operator T2*() const {
return static_cast<T2*>(ptr);
}
/**
* Returns the plain pointer to the pointed object. The object will
* still be managed by the SmartPtr. You must ensure that the pointer
* is valid (so don't delete the SmartPtr before you are done with the
* plain pointer).
* @return the plain pointer
* @see data()
* @see release()
* @see WeakPtr
*/
template<class T2>
operator const T2*() const {
return static_cast<const T2*>(ptr);
}
/**
* Returns a reference to the pointed object. This works exactly
* like on a regular pointer.
* @return the pointer object
*/
T& operator*() {
return *ptr;
}
/**
* Returns a reference to the pointed object. This works exactly
* like on a regular pointer.
* @return the pointer object
*/
const T& operator*() const {
return *ptr;
}
/**
* Access a member of the pointed object. This works exactly
* like on a regular pointer.
* @return the pointer
*/
T* operator->() {
return ptr;
}
/**
* Access a member of the pointed object. This works exactly
* like on a regular pointer.
* @return the pointer
*/
const T* operator->() const {
return ptr;
}
/**
* Compares two SmartPtrs. They are equal if both point to the
* same object.
* @return true if both point to the same object
*/
bool operator==(const SmartPtr<T>& sptr) const {
return ptr == sptr.ptr;
}
/**
* Compares two SmartPtrs. They are unequal if both point to
* different objects.
* @return true if both point to different objects
*/
bool operator!=(const SmartPtr<T>& sptr) const {
return ptr != sptr.ptr;
}
/**
* Compares a SmartPtr with a plain pointer. They are equal if
* both point to the same object.
* @return true if both point to the same object
*/
bool operator==(const T* p) const {
return ptr == p;
}
/**
* Compares a SmartPtr with a plain pointer. They are unequal if
* both point to different objects.
* @return true if both point to different objects
*/
bool operator!=(const T* p) const {
return ptr != p;
}
/**
* Negates the pointer. True if the pointer is the null pointer
* @return true for the null pointer, false otherwise
*/
bool operator!() const {
return ptr == 0;
}
/**
* Returns the pointer. The object will still be managed
* by the SmartPtr. You must ensure that the pointer
* is valid (so don't delete the SmartPtr before you are done with the
* plain pointer).
* @return the plain pointer
* @see release()
* @see WeakPtr
*/
T* data() {
return ptr;
}
/**
* Returns the pointer. The object will still be managed
* by the SmartPtr. You must ensure that the pointer
* is valid (so don't delete the SmartPtr before you are done with the
* plain pointer).
* @return the plain pointer
* @see release()
* @see WeakPtr
*/
const T* data() const {
return ptr;
}
/**
* Checks whether both SmartPtrs use the same pointer but two
* different reference counts.
* If yes, one of them must be 0 (object released), otherwise
* it is an error.
* @return true if the pointers are used correctly
*/
bool isRCCorrect(const SmartPtr<T> &p2) const {
if (ptr == p2.ptr)
return true;
if (rc == p2.rc)
return true;
return (rc->refsToObject == 0) || (p2.rc->refsToObject == 0);
}
/**
* Returns the reference count of the object. The count is 0 if
* the object has been released (@ref release()). For the null pointer
* the reference count is always 1.
* @return the reference count, or 0 for released objects
*/
int referenceCount() const {
return rc ? rc->refsToObject : 1;
}
/**
* Returns a string representation of the pointer.
* @return a string representation
*/
TQString toString() const {
int objrcount = 1;
int rcrcount = 0;
if (rc) {
objrcount = rc->refsToObject;
rcrcount = rc->refsToThis;
}
return TQString("SmartPtr: ptr=%1, refcounts=%2, ptrnum=%3")
.arg((int)ptr).arg(objrcount).arg(rcrcount);
}
};
#endif