Remove QMetaObject guards and deprecate QPointer.

QWeakPointer is superior and preferred.

Remove QMetaObject::addGuard(), QMetaObject::changeGuard(),
QMetaObject::removeGuard(), and QObjectPrivate::clearGuards().

Implement QPointer using QWeakPointer<T> instead. This changes the
behavior of QPointer in 2 ways:

- During destruction of a QWidget. Previously, the destructor of QWidget
would reset all QPointers so that they would return zero when destroying
children. Update tst_QPointer to account for this change.

- When constructing a QSharedPointer to take ownership of an object
after a QPointer is already tracking the object. Previously, the shared
pointer construction would not be affected by the QPointer, but now
that QPointer is implemented using QWeakPoiner, constructing the
QSharedPointer will cause an abort(). Fix tst_QSharedPointer by
removing the use of QPointer in the objectCast() test.

These behavior changes are documented in the QPointer class
documentation and in the changes file.

Change-Id: I92d0276219c076ece7bcb60f6e1b9120ce4f5747
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
Bradley T. Hughes 2011-11-22 14:45:35 +01:00 committed by Qt by Nokia
parent a6ae75f92a
commit 6f0f9f6928
11 changed files with 63 additions and 154 deletions

18
dist/changes-5.0.0 vendored
View File

@ -267,3 +267,21 @@ Qt for Windows CE
* Important Behavior Changes *
****************************************************************************
- QPointer
* QPointer itself is now deprecated, and the implementation of QPointer
has been changed to use QWeakPointer. The old guard mechanism has been
removed. This causes two slight changes in behavior when using
QPointer:
* When using QPointer on a QWidget (or a subclass of QWidget), previously
the QPointer would be cleared by the QWidget destructor. Now, the QPointer
is cleared by the QObject destructor (since this is when QWeakPointers are
cleared). Any QPointers tracking a widget will NOT be cleared before the
QWidget destructor destroys the children for the widget being tracked.
* When constructing a QSharedPointer to take ownership of an object after a
QPointer is already tracking the object. Previously, the shared pointer
construction would not be affected by the QPointer, but now that QPointer
is implemented using QWeakPoiner, constructing the QSharedPointer will
cause an abort().

View File

@ -177,7 +177,6 @@ QObjectPrivate::QObjectPrivate(int version)
deleteWatch = 0;
#endif
metaObject = 0;
hasGuards = false;
isWindow = false;
}
@ -409,113 +408,6 @@ void QObjectPrivate::cleanConnectionLists()
}
}
typedef QMultiHash<QObject *, QObject **> GuardHash;
Q_GLOBAL_STATIC(GuardHash, guardHash)
Q_GLOBAL_STATIC(QMutex, guardHashLock)
/*!\internal
*/
void QMetaObject::addGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash *hash = guardHash();
if (!hash) {
*ptr = 0;
return;
}
QMutexLocker locker(guardHashLock());
QObjectPrivate::get(*ptr)->hasGuards = true;
hash->insert(*ptr, ptr);
}
/*!\internal
*/
void QMetaObject::removeGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash *hash = guardHash();
/* check that the hash is empty - otherwise we might detach
the shared_null hash, which will alloc, which is not nice */
if (!hash || hash->isEmpty())
return;
QMutexLocker locker(guardHashLock());
if (!*ptr) //check again, under the lock
return;
GuardHash::iterator it = hash->find(*ptr);
const GuardHash::iterator end = hash->end();
bool more = false; //if the QObject has more pointer attached to it.
for (; it.key() == *ptr && it != end; ++it) {
if (it.value() == ptr) {
it = hash->erase(it);
if (!more) more = (it != end && it.key() == *ptr);
break;
}
more = true;
}
if (!more)
QObjectPrivate::get(*ptr)->hasGuards = false;
}
/*!\internal
*/
void QMetaObject::changeGuard(QObject **ptr, QObject *o)
{
GuardHash *hash = guardHash();
if (!hash) {
*ptr = 0;
return;
}
QMutexLocker locker(guardHashLock());
if (o) {
hash->insert(o, ptr);
QObjectPrivate::get(o)->hasGuards = true;
}
if (*ptr) {
bool more = false; //if the QObject has more pointer attached to it.
GuardHash::iterator it = hash->find(*ptr);
const GuardHash::iterator end = hash->end();
for (; it.key() == *ptr && it != end; ++it) {
if (it.value() == ptr) {
it = hash->erase(it);
if (!more) more = (it != end && it.key() == *ptr);
break;
}
more = true;
}
if (!more)
QObjectPrivate::get(*ptr)->hasGuards = false;
}
*ptr = o;
}
/*! \internal
*/
void QObjectPrivate::clearGuards(QObject *object)
{
GuardHash *hash = 0;
QMutex *mutex = 0;
QT_TRY {
hash = guardHash();
mutex = guardHashLock();
} QT_CATCH(const std::bad_alloc &) {
// do nothing in case of OOM - code below is safe
}
/* check that the hash is empty - otherwise we might detach
the shared_null hash, which will alloc, which is not nice */
if (hash && !hash->isEmpty()) {
QMutexLocker locker(mutex);
GuardHash::iterator it = hash->find(object);
const GuardHash::iterator end = hash->end();
while (it.key() == object && it != end) {
*it.value() = 0;
it = hash->erase(it);
}
}
}
/*! \internal
*/
QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction,
@ -840,12 +732,6 @@ QObject::~QObject()
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
if (d->hasGuards && !d->isWidget) {
// set all QPointers for this object to zero - note that
// ~QWidget() does this for us, so we don't have to do it twice
QObjectPrivate::clearGuards(this);
}
QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
if (sharedRefcount) {
if (sharedRefcount->strongref.load() > 0) {

View File

@ -105,9 +105,8 @@ public:
uint receiveChildEvents : 1;
uint inEventHandler : 1; //only used if QT_JAMBI_BUILD
uint inThreadChangeEvent : 1;
uint hasGuards : 1; //true iff there is one or more QPointer attached to this object
uint isWindow : 1; //for QWindow
uint unused : 21;
uint unused : 22;
int postedEvents;
QMetaObject *metaObject; // assert dynamic
};

View File

@ -183,7 +183,6 @@ public:
static int *setDeleteWatch(QObjectPrivate *d, int *newWatch);
static void resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch);
#endif
static void clearGuards(QObject *);
static QObjectPrivate *get(QObject *o) {
return o->d_func();

View File

@ -341,11 +341,6 @@ struct Q_CORE_EXPORT QMetaObject
static void activate(QObject *sender, int signal_index, void **argv);
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
// internal guarded pointers
static void addGuard(QObject **ptr);
static void removeGuard(QObject **ptr);
static void changeGuard(QObject **ptr, QObject *o);
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,

View File

@ -44,7 +44,7 @@
\brief The QPointer class is a template class that provides guarded pointers to QObject.
\ingroup objectmodel
\obsolete Use QWeakPointer instead.
A guarded pointer, QPointer<T>, behaves like a normal C++
pointer \c{T *}, except that it is automatically set to 0 when the
@ -57,6 +57,24 @@
destroyed while you still hold a reference to it. You can safely
test the pointer for validity.
Note that Qt 5 introduces two slight changes in behavior when using QPointer.
\list
\i When using QPointer on a QWidget (or a subclass of QWidget), previously
the QPointer would be cleared by the QWidget destructor. Now, the QPointer
is cleared by the QObject destructor (since this is when QWeakPointers are
cleared). Any QPointers tracking a widget will \b NOT be cleared before the
QWidget destructor destroys the children for the widget being tracked.
\i When constructing a QSharedPointer to take ownership of an object after a
QPointer is already tracking the object. Previously, the shared pointer
construction would not be affected by the QPointer, but now that QPointer
is implemented using QWeakPoiner, constructing the QSharedPointer will
cause an \c abort().
\endlist
Qt also provides QSharedPointer, an implementation of a reference-counted
shared pointer object, which can be used to maintain a collection of
references to an individual pointer.

View File

@ -42,7 +42,7 @@
#ifndef QPOINTER_H
#define QPOINTER_H
#include <QtCore/qobject.h>
#include <QtCore/qsharedpointer.h>
QT_BEGIN_HEADER
@ -50,34 +50,35 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Core)
#if QT_DEPRECATED_SINCE(5,0)
template <class T>
class QPointer
class QT_DEPRECATED QPointer
{
QObject *o;
QWeakPointer<T> wp;
public:
inline QPointer() : o(0) {}
inline QPointer(T *p) : o(p)
{ QMetaObject::addGuard(&o); }
inline QPointer(const QPointer<T> &p) : o(p.o)
{ QMetaObject::addGuard(&o); }
inline ~QPointer()
{ QMetaObject::removeGuard(&o); }
inline QPointer() : wp() { }
inline QPointer(T *p) : wp(p) { }
inline QPointer(const QPointer<T> &p) : wp(p.wp) { }
inline ~QPointer() { }
inline QPointer<T> &operator=(const QPointer<T> &p)
{ if (this != &p) QMetaObject::changeGuard(&o, p.o); return *this; }
{ wp = p.wp; return *this; }
inline QPointer<T> &operator=(T* p)
{ if (o != p) QMetaObject::changeGuard(&o, p); return *this; }
{ wp = p; return *this; }
inline bool isNull() const
{ return !o; }
{ return wp.isNull(); }
inline T* operator->() const
{ return static_cast<T*>(const_cast<QObject*>(o)); }
{ return wp.data(); }
inline T& operator*() const
{ return *static_cast<T*>(const_cast<QObject*>(o)); }
{ return *wp.data(); }
inline operator T*() const
{ return static_cast<T*>(const_cast<QObject*>(o)); }
{ return wp.data(); }
inline T* data() const
{ return static_cast<T*>(const_cast<QObject*>(o)); }
{ return wp.data(); }
};
@ -161,6 +162,8 @@ inline bool operator!= (int i, const QPointer<T> &p)
{ Q_ASSERT(i == 0); return !i && !p.isNull(); }
#endif
#endif // QT_DEPRECATED_SINCE(5,0)
QT_END_NAMESPACE
QT_END_HEADER

View File

@ -1185,22 +1185,19 @@ void QAction::activate(ActionEvent event)
{
Q_D(QAction);
if(event == Trigger) {
QObject *guard = this;
QMetaObject::addGuard(&guard);
QWeakPointer<QObject> guard = this;
if(d->checkable) {
// the checked action of an exclusive group cannot be unchecked
if (d->checked && (d->group && d->group->isExclusive()
&& d->group->checkedAction() == this)) {
if (guard)
if (!guard.isNull())
emit triggered(true);
QMetaObject::removeGuard(&guard);
return;
}
setChecked(!d->checked);
}
if (guard)
if (!guard.isNull())
emit triggered(d->checked);
QMetaObject::removeGuard(&guard);
} else if(event == Hover) {
emit hovered();
}

View File

@ -1469,10 +1469,6 @@ QWidget::~QWidget()
delete d->needsFlush;
d->needsFlush = 0;
// set all QPointers for this object to zero
if (d->hasGuards)
QObjectPrivate::clearGuards(this);
if (d->declarativeData) {
QAbstractDeclarativeData::destroyed(d->declarativeData, this);
d->declarativeData = 0; // don't activate again in ~QObject

View File

@ -241,8 +241,8 @@ public:
ChildWidget::~ChildWidget()
{
QCOMPARE(static_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0));
QCOMPARE(qobject_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0));
QCOMPARE(static_cast<QWidget *>(guardedPointer), parentWidget());
QCOMPARE(qobject_cast<QWidget *>(guardedPointer), parentWidget());
}
class DerivedChild;

View File

@ -767,7 +767,6 @@ void tst_QSharedPointer::objectCast()
{
OtherObject *data = new OtherObject;
QPointer<OtherObject> qptr = data;
QSharedPointer<OtherObject> ptr = QSharedPointer<OtherObject>(data);
QWeakPointer<QObject> weakptr = ptr;
@ -788,7 +787,6 @@ void tst_QSharedPointer::objectCast()
// drop the reference:
ptr.clear();
QVERIFY(ptr.isNull());
QVERIFY(qptr.isNull());
QVERIFY(weakptr.toStrongRef().isNull());
// verify that the object casts fail without crash