• Qt源码分析之QPointer


    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中哈希表的

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

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

  • 相关阅读:
    程序、进程、线程区别与联系
    SQL常用知识与必须掌握的面试常问SQL语句
    快速搭建一个基于react的项目
    原生js判断设备类型
    在vue项目中设置BASE_URL
    纯前端实现数据导出excel文件
    原生js实现拖拽功能
    使用Echarts实现折线图的一点总结
    在vue项目中显示实时时间(年月日时分秒)
    在vue项目中使用MD5.js
  • 原文地址:https://www.cnblogs.com/findumars/p/5924916.html
Copyright © 2020-2023  润新知