Qt源码分析之QPointer

原文:http://blog.csdn.net/oowgsoo/article/details/1529424

QPointer是一个指针封装类,其作用类似于智能指针,但是它最大的特点应该是在指针的控制上,它希望一个Qt的指针(当然是从QObject派生的)可以同时被多个类拥有,这在
界面编程中当然是很常见的事情了,但是当这个指针被删除时,我们不希望再找到那两个界面类然后通知它们,相反我们希望这两个界面类可以直接判断QPointer中的isNull方法
很自然的知道原始指针已经不存在了

1.试验代码:

#include <QApplication>
#include
<QPushButton>
#include
<QPointer>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QPushButton
* pButton = new QPushButton("wgs");
QPointer
<QPushButton> button = pButton;
delete pButton;
if (!button.isNull())
{
button
->setText("www");
}
return app.exec();
}

一段很短的代码,这里需要的注意的是QPointer指针的使用

template <class T>
class QPointer
{
QObject
*o;
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
<T> &operator=(const QPointer<T> &p)
{
if (this != &p) QMetaObject::changeGuard(&o, p.o); return *this; }
inline QPointer
<T> &operator=(T* p)
{
if (o != p) QMetaObject::changeGuard(&o, p); return *this; }

inline
bool isNull() const
{
return !o; }

inline T
* operator->() const
{
return static_cast<T*>(const_cast<QObject*>(o)); }
inline T
& operator*() const
{
return *static_cast<T*>(const_cast<QObject*>(o)); }
inline
operator T*() const
{
return static_cast<T*>(const_cast<QObject*>(o)); }
};

QPointer只是一个简单的模板,对QMetaObject的相关操作做了简单的封装,这里的基本思想是
在QPointer构造的时候调用QMetaObject::addGuard(&o),把T的指针加入QMetaObject内的一个哈希表中,
在QPointer析构的时候调用QMetaObject::removeGuard(&o),把T的指针从哈希表中删除
这是一个QMetaObject中静态成员,该哈希表的定义如下:
typedef QMultiHash<QObject *, QObject **> GuardHash;
这个哈希表存储的是指针的值和指针的地址,因此加入的代码如下:

void QMetaObject::addGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash
*hash = guardHash();
if (!hash) {
*ptr = 0;
return;
}
QWriteLocker locker(guardHashLock());
hash
->insert(*ptr, ptr);
}

为什么不是只保存一个指针呢,原来是为了防止误删除,看看删除的代码:

void QMetaObject::removeGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash
*hash = guardHash();
if (!hash)
return;
QWriteLocker locker(guardHashLock());
GuardHash::iterator it
= hash->find(*ptr);
const GuardHash::iterator end = hash->end();
for (; it.key() == *ptr && it != end; ++it) {
if (it.value() == ptr) {
(
void) hash->erase(it);
break;
}
}
}

只有在it.value() == ptr的时候才会删除

但是等等,当删除普通指针时又如何更新这个哈希表呢?
delete pButton;如何通知QMetaObject中的哈希表更新?
答案是在QObject中,请注意QObject的析构函数

QObject::~QObject()
{
Q_D(QObject);
if (d->wasDeleted) {
#if defined(QT_DEBUG)
qWarning(
"Double QObject deletion detected");
#endif
return;
}
d
->wasDeleted = true;

d
->blockSig = 0; // unblock signals so we always emit destroyed()

// set all QPointers for this object to zero
GuardHash *hash = ::guardHash();
if (hash) {
QWriteLocker locker(guardHashLock());
GuardHash::iterator it
= hash->find(this);
const GuardHash::iterator end = hash->end();
while (it.key() == this && it != end) {
*it.value() = 0;
it
= hash->erase(it);
}
}

emit destroyed(
this);

QConnectionList
*list = ::connectionList();
if (list) {
QWriteLocker locker(
&list->lock);
list
->remove(this);
}

if (d->pendTimer) {
// have pending timers
QThread *thr = thread();
if (thr || d->thread == 0) {
// don't unregister timers in the wrong thread
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(thr);
if (eventDispatcher)
eventDispatcher
->unregisterTimers(this);
}
}

d
->eventFilters.clear();

// delete children objects
if (!d->children.isEmpty()) {
qDeleteAll(d
->children);
d
->children.clear();
}


{
QWriteLocker locker(QObjectPrivate::readWriteLock());
::qt_removeObject(
this);

/*
theoretically, we cannot check d->postedEvents without
holding the postEventList.mutex for the object's thread,
but since we hold the QObjectPrivate::readWriteLock(),
nothing can go into QCoreApplication::postEvent(), which
effectively means noone can post new events, which is what
we are trying to prevent. this means we can safely check
d->postedEvents, since we are fairly sure it will not
change (it could, but only by decreasing, i.e. removing
posted events from a differebnt thread)
*/
if (d->postedEvents > 0)
QCoreApplication::removePostedEvents(
this);
}

if (d->parent) // remove it from parent object
d->setParent_helper(0);

delete d;
d_ptr
= 0;
}

这里做了很多很多的事情,其中的代码

   // set all QPointers for this object to zero
GuardHash *hash = ::guardHash();
if (hash) {
QWriteLocker locker(guardHashLock());
GuardHash::iterator it
= hash->find(this);
const GuardHash::iterator end = hash->end();
while (it.key() == this && it != end) {
*it.value() = 0;
it
= hash->erase(it);
}
}

就是更新QMetaObject中哈希表的

这也就是单基类的好处,可以在这里控制很多事情 

posted on 2011-08-27 14:55  hicjiajia  阅读(1604)  评论(0编辑  收藏  举报