zoukankan      html  css  js  c++  java
  • 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中哈希表的

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

  • 相关阅读:
    1026 Table Tennis (30)
    1029 Median
    1025 PAT Ranking (25)
    1017 Queueing at Bank (25)
    1014 Waiting in Line (30)
    1057 Stack (30)
    1010 Radix (25)
    1008 Elevator (20)
    字母大小写转换
    Nmap的基础知识
  • 原文地址:https://www.cnblogs.com/cute/p/2107528.html
Copyright © 2011-2022 走看看