Add QEnableSharedFromThis class

It enables you to get a valid QSharedPointer instance to 'this',
when all you have is 'this'.

Task-number: QTBUG-7287
Change-Id: I3ed1c9c4d6b110fe02302312cc3c4a75e9d95a0c
Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
Roman Pasechnik 2013-07-02 21:35:21 +03:00 committed by Giuseppe D'Angelo
parent bc19ab03b2
commit 90a68926f3
4 changed files with 326 additions and 0 deletions

View File

@ -371,6 +371,43 @@
\sa QSharedPointer, QScopedPointer \sa QSharedPointer, QScopedPointer
*/ */
/*!
\class QEnableSharedFromThis
\inmodule QtCore
\brief A base class that allows to obtain a QSharedPointer for an object already managed by a shared pointer
\since 5.4
You can inherit this class when you need to create a QSharedPointer
from any instance of a class -- for instance, from within the
object itself. The key point is that the "obvious" technique of
just returning QSharedPointer<T>(this) can not be used, because
this winds up creating multiple distinct QSharedPointer objects
with separate reference counts. For this reason you must never
create more than one QSharedPointer from the same raw pointer.
QEnableSharedFromThis defines two member functions called
sharedFromThis() that return a QSharedPointer<T> and
QSharedPointer<const T>, depending on constness, to \c this:
\code
class Y: public QEnableSharedFromThis<Y>
{
public:
QSharedPointer<Y> f()
{
return sharedFromThis();
}
};
int main()
{
QSharedPointer<Y> p(new Y());
QSharedPointer<Y> y = p->f();
Q_ASSERT(p == y); // p and q must share ownership
}
\endcode
*/
/*! /*!
\fn QSharedPointer::QSharedPointer() \fn QSharedPointer::QSharedPointer()
@ -923,6 +960,23 @@
may have had to the pointer. may have had to the pointer.
*/ */
/*!
\fn QSharedPointer<T> QEnableSharedFromThis::sharedFromThis()
\since 5.4
If \c this (that is, the subclass instance invoking this method) is being
managed by a QSharedPointer, returns a shared pointer instance pointing to
\c this; otherwise returns a QSharedPointer holding a null pointer.
*/
/*!
\fn QSharedPointer<const T> QEnableSharedFromThis::sharedFromThis() const
\overload
\since 5.4
Const overload of sharedFromThis().
*/
/*! /*!
\fn bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2) \fn bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
\relates QSharedPointer \relates QSharedPointer

View File

@ -131,6 +131,14 @@ public:
QSharedPointer<T> lock() const; QSharedPointer<T> lock() const;
}; };
template <class T>
class QEnableSharedFromThis
{
public:
QSharedPointer<T> sharedFromThis();
QSharedPointer<const T> sharedFromThis() const;
};
template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2); template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2);
template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2); template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2);
template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const X *ptr2); template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const X *ptr2);

View File

@ -91,6 +91,7 @@ template<typename T> inline void qt_sharedpointer_cast_check(T *) { }
// //
template <class T> class QWeakPointer; template <class T> class QWeakPointer;
template <class T> class QSharedPointer; template <class T> class QSharedPointer;
template <class T> class QEnableSharedFromThis;
class QVariant; class QVariant;
@ -479,6 +480,14 @@ private:
delete d; delete d;
} }
template <class X>
inline void enableSharedFromThis(const QEnableSharedFromThis<X> *ptr)
{
ptr->initializeFromSharedPointer(*this);
}
inline void enableSharedFromThis(...) {}
template <typename Deleter> template <typename Deleter>
inline void internalConstruct(T *ptr, Deleter deleter) inline void internalConstruct(T *ptr, Deleter deleter)
{ {
@ -499,6 +508,7 @@ private:
internalSafetyCheckAdd(d, ptr); internalSafetyCheckAdd(d, ptr);
#endif #endif
d->setQObjectShared(ptr, true); d->setQObjectShared(ptr, true);
enableSharedFromThis(ptr);
} }
template <class X> template <class X>
@ -707,6 +717,37 @@ public:
T *value; T *value;
}; };
template <class T>
class QEnableSharedFromThis
{
protected:
#ifdef Q_COMPILER_DEFAULT_MEMBERS
QEnableSharedFromThis() = default;
#else
Q_DECL_CONSTEXPR QEnableSharedFromThis() {}
#endif
QEnableSharedFromThis(const QEnableSharedFromThis &) {}
QEnableSharedFromThis &operator=(const QEnableSharedFromThis &) { return *this; }
public:
inline QSharedPointer<T> sharedFromThis() { return QSharedPointer<T>(weakPointer); }
inline QSharedPointer<const T> sharedFromThis() const { return QSharedPointer<const T>(weakPointer); }
#ifndef Q_NO_TEMPLATE_FRIENDS
private:
template <class X> friend class QSharedPointer;
#else
public:
#endif
template <class X>
inline void initializeFromSharedPointer(const QSharedPointer<X> &ptr) const
{
weakPointer = ptr;
}
mutable QWeakPointer<T> weakPointer;
};
// //
// operator== and operator!= // operator== and operator!=
// //

View File

@ -112,6 +112,7 @@ private slots:
void invalidConstructs(); void invalidConstructs();
void qvariantCast(); void qvariantCast();
void sharedFromThis();
public slots: public slots:
void cleanup() { safetyCheck(); } void cleanup() { safetyCheck(); }
@ -2141,6 +2142,228 @@ void tst_QSharedPointer::qvariantCast()
} }
} }
class SomeClass : public QEnableSharedFromThis<SomeClass>
{
public:
SomeClass()
{
}
QSharedPointer<SomeClass> getSharedPtr()
{
return sharedFromThis();
}
QSharedPointer<const SomeClass> getSharedPtr() const
{
return sharedFromThis();
}
Data data;
};
void tst_QSharedPointer::sharedFromThis()
{
const int generations = Data::generationCounter;
const int destructions = Data::destructorCounter;
{
SomeClass sc;
QSharedPointer<SomeClass> scp = sc.sharedFromThis();
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
QVERIFY(const_scp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
}
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions + 1);
{
const SomeClass sc;
QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
QVERIFY(const_scp.isNull());
QCOMPARE(Data::generationCounter, generations + 2);
QCOMPARE(Data::destructorCounter, destructions + 1);
}
QCOMPARE(Data::generationCounter, generations + 2);
QCOMPARE(Data::destructorCounter, destructions + 2);
{
SomeClass *sc = new SomeClass;
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp;
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp = sc->sharedFromThis();
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp = QSharedPointer<SomeClass>(sc);
QVERIFY(!scp.isNull());
QCOMPARE(scp.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp2;
QVERIFY(scp2.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp2 = sc->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(scp2.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<const SomeClass> scp3;
QVERIFY(scp3.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp3 = sc->sharedFromThis();
QVERIFY(!scp3.isNull());
QVERIFY(scp == scp3);
QVERIFY(scp2 == scp3);
QCOMPARE(scp3.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp4;
QVERIFY(scp4.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp4 = sc->getSharedPtr();
QVERIFY(!scp4.isNull());
QVERIFY(scp == scp4);
QVERIFY(scp2 == scp4);
QVERIFY(scp3 == scp4);
QCOMPARE(scp4.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<const SomeClass> scp5;
QVERIFY(scp5.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp5 = const_cast<const SomeClass *>(sc)->getSharedPtr();
QVERIFY(!scp4.isNull());
QVERIFY(scp == scp5);
QVERIFY(scp2 == scp5);
QVERIFY(scp3 == scp5);
QVERIFY(scp4 == scp5);
QCOMPARE(scp5.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
}
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 3);
QSharedPointer<SomeClass> scp;
QVERIFY(scp.isNull());
{
QSharedPointer<SomeClass> scp2(new SomeClass());
QVERIFY(!scp2.isNull());
scp = scp2->sharedFromThis();
QVERIFY(!scp.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<const SomeClass> scp2;
scp2 = scp->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<SomeClass> scp2;
scp2 = scp->getSharedPtr();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<const SomeClass> scp2;
scp2 = qSharedPointerConstCast<const SomeClass>(scp)->getSharedPtr();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<SomeClass> scp2;
scp2 = scp->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
scp2.clear();
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
QVERIFY(scp2.isNull());
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
scp.clear();
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 4);
}
namespace ReentrancyWhileDestructing { namespace ReentrancyWhileDestructing {
struct IB struct IB
{ {