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:
parent
bc19ab03b2
commit
90a68926f3
@ -371,6 +371,43 @@
|
||||
\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()
|
||||
|
||||
@ -923,6 +960,23 @@
|
||||
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)
|
||||
\relates QSharedPointer
|
||||
|
@ -131,6 +131,14 @@ public:
|
||||
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 X *ptr2);
|
||||
|
@ -91,6 +91,7 @@ template<typename T> inline void qt_sharedpointer_cast_check(T *) { }
|
||||
//
|
||||
template <class T> class QWeakPointer;
|
||||
template <class T> class QSharedPointer;
|
||||
template <class T> class QEnableSharedFromThis;
|
||||
|
||||
class QVariant;
|
||||
|
||||
@ -479,6 +480,14 @@ private:
|
||||
delete d;
|
||||
}
|
||||
|
||||
template <class X>
|
||||
inline void enableSharedFromThis(const QEnableSharedFromThis<X> *ptr)
|
||||
{
|
||||
ptr->initializeFromSharedPointer(*this);
|
||||
}
|
||||
|
||||
inline void enableSharedFromThis(...) {}
|
||||
|
||||
template <typename Deleter>
|
||||
inline void internalConstruct(T *ptr, Deleter deleter)
|
||||
{
|
||||
@ -499,6 +508,7 @@ private:
|
||||
internalSafetyCheckAdd(d, ptr);
|
||||
#endif
|
||||
d->setQObjectShared(ptr, true);
|
||||
enableSharedFromThis(ptr);
|
||||
}
|
||||
|
||||
template <class X>
|
||||
@ -707,6 +717,37 @@ public:
|
||||
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!=
|
||||
//
|
||||
|
@ -112,6 +112,7 @@ private slots:
|
||||
void invalidConstructs();
|
||||
|
||||
void qvariantCast();
|
||||
void sharedFromThis();
|
||||
|
||||
public slots:
|
||||
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 {
|
||||
struct IB
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user