• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            逛奔的蝸牛

            我不聰明,但我會很努力

               ::  :: 新隨筆 ::  ::  :: 管理 ::

            轉(zhuǎn)自: http://blog.csdn.net/oowgsoo/archive/2007/03/14/1529284.aspx

            Qt的QObject

            1.試驗代碼:
            #include <QApplication>
            #include <QtCore>
            #include <QtGui>

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

             int size = sizeof(QObject);

             QPushButton* quit = new QPushButton("Quit");
             delete quit;

             return app.exec();
            }


            QObject是Qt類體系的唯一基類,就象MFC中的CObject和Dephi中的TObject,是Qt各種功能的源頭活水,因此Qt源碼分析的第一節(jié)就放在這個QObject上
             int size = sizeof(QObject);
            QObject的大小是8,除了虛函數(shù)表指針需要的4個字節(jié)以外,另外的4個字節(jié)是:
                QObjectData *d_ptr;
            QObject中的數(shù)據(jù)被封裝在QObjectData類中了,為什么要封裝數(shù)據(jù)呢?
            原因是Qt中有一個很重要的設(shè)計模式就是句柄實體模式,也就是以QObject為基類的類一般都是句柄類,一般只有一個指針指向一個實體類,在實體類中保存全部的數(shù)據(jù)
            而且一般情況下這個指針還是私有的,方便以后修改句柄類的實現(xiàn)細(xì)節(jié)
            因此,也可以說和句柄類繼承關(guān)系平行的也有一套實體類派生體系,因此,準(zhǔn)確的說,Qt的基類其實有兩個,一個是QObject,這是句柄類的唯一基類,另一個是QObjectData,這是實體
            類的基類

            QObjectData類定義如下:
            class QObjectData {
            public:
                virtual ~QObjectData() = 0;
                QObject *q_ptr;
                QObject *parent;
                QObjectList children;

                uint isWidget : 1;
                uint pendTimer : 1;
                uint blockSig : 1;
                uint wasDeleted : 1;
                uint ownObjectName : 1;
                uint sendChildEvents : 1;
                uint receiveChildEvents : 1;
                uint unused : 25;
                int postedEvents;
            #ifdef QT3_SUPPORT
                int postedChildInsertedEvents;
            #else
                int reserved;
            #endif
            };
            QObject *q_ptr;
            這個指針指向?qū)嶓w類對應(yīng)的句柄類,這和上面的代碼
            QObjectData *d_ptr;
            遙相呼應(yīng),使得句柄類和實體類可以雙向的引用,為什么是這樣的命名方式呢?可能q指的是Qt接口類,d指的是Data數(shù)據(jù)類,這當(dāng)然是猜測了,但是或許可以方便你記憶,在Qt中,
            這兩個指針名字是非常重要的,必須記住

            但是僅僅如此還是不容易使用這兩個指針,因為它們都是基類的類型,難道每次使用都要類型轉(zhuǎn)換嗎?為了簡單起見,Qt在這里聲明了兩個宏

            #define Q_DECLARE_PRIVATE(Class) \
                inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } \
                inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } \
                friend class Class##Private;

            #define Q_DECLARE_PUBLIC(Class) \
                inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
                inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
                friend class Class;
            只要在類的頭文件中使用這兩個宏,就可以通過函數(shù)直接得到實體類和句柄類的實際類型了,而且這里還聲明了友元,使得數(shù)據(jù)類和句柄類連訪問權(quán)限也不用顧忌了

            而且為了cpp文件中調(diào)用的方便,更是直接聲明了以下兩個宏
            #define Q_D(Class) Class##Private * const d = d_func()
            #define Q_Q(Class) Class * const q = q_func()
            好了,使用起來倒是方便了,但是以后局部變量可千萬不能聲明為d和q了

            這里的d_func和q_func函數(shù)是非常常用的函數(shù),可以理解為一個是得到數(shù)據(jù)類,一個是得到Qt接口類

            QObject *parent;
            這里指向QObject的父類
            QObjectList children;
            這里指向QObject相關(guān)的子類列表
            這確實是個大膽的設(shè)計,如果系統(tǒng)中產(chǎn)生了1000000個QObject實例(對于大的系統(tǒng),這個數(shù)字很容易達(dá)到吧),每個QObject子類平均下來是100(這個數(shù)字可能大了),
            光這些指針的開銷就有1000000*100*4=400M,是夠恐怖的,如果我們必須在靈活性和運行開銷之間做一個選擇的話,無疑Qt選擇了前者,對此我也很難評論其中的優(yōu)劣,
            還是祈求越來越強的硬件水平和Qt這么多年來得到的赫赫威名保佑我們根本就沒有這個問題吧,呵呵
            總之,Qt確實在內(nèi)存中保存了所有類實例的樹型結(jié)構(gòu)

                uint isWidget : 1;
                uint pendTimer : 1;
                uint blockSig : 1;
                uint wasDeleted : 1;
                uint ownObjectName : 1;
                uint sendChildEvents : 1;
                uint receiveChildEvents : 1;
                uint unused : 25;
            這些代碼就簡單了,主要是一些標(biāo)記位,為了節(jié)省內(nèi)存開銷,這里采用了位域的語法,還保留了25位為unused,留做以后的擴充
            #ifdef QT3_SUPPORT
                int postedChildInsertedEvents;
            #else
                int reserved;
            #endif
            這里或許是為了兼容Qt3下序列化的數(shù)據(jù)吧,即使沒有定義QT3_SUPPORT,還是保留了一個數(shù)據(jù)reserved,以保證整個QObjectData的大小不變

            具體看一個例子吧,對這種句柄實體模式加深認(rèn)識,這就是Qt中的按鈕類QPushButton
            QPushButton的句柄類派生關(guān)系是:
            QObject
             QWidget
              QAbstractButton
               QPushButton
               
            QPushButton的實體類派生關(guān)系是:
            QObjectData
             QObjectPrivate
              QWidgetPrivate
               QAbstractButtonPrivate
                QPushButtonPrivate

            可以看出,這里確實是一個平行體系,只不過實體類派生關(guān)系中多了一個QObjectPrivate,這個類封裝了線程處理,信號和槽機制等具體的實現(xiàn),可以說它才是Qt實體類中
            真正起作用的基類,而QObjectData不過是一層淺淺的數(shù)據(jù)封裝而已

            先不忙了解QObjectPrivate類中的接口和實現(xiàn),我們先看看在Qt中,句柄類和實體類這兩條體系是如何構(gòu)造的?
             QPushButton* quit = new QPushButton("Quit");
            創(chuàng)建一個Qt的按鈕,簡簡單單一行代碼,其實背后大有玄機
            QPushButton::QPushButton(const QString &text, QWidget *parent)
                : QAbstractButton(*new QPushButtonPrivate, parent)
            首先QPushButton的構(gòu)造函數(shù)中調(diào)用了QAbstractButton的構(gòu)造函數(shù),同時馬上new出來一個QPushButtonPrivate實體類,然后把指針轉(zhuǎn)換為引用傳遞給QAbstractButton

            QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
                : QWidget(dd, parent, 0)
            QAbstractButton的構(gòu)造函數(shù)中繼續(xù)調(diào)用基類QWidget的構(gòu)造函數(shù),同時把QPushButtonPrivate實體類指針繼續(xù)傳給基類

            QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)
                : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice()
            QWidget繼續(xù)坐著同樣的事情

            QObject::QObject(QObjectPrivate &dd, QObject *parent)
                : d_ptr(&dd)
            終于到了基類QObject,這里就直接把QPushButtonPrivate的指針賦值給了d_ptr(還記得這個變量名稱吧)

            最終在QPushButton構(gòu)造時同時產(chǎn)生的new QPushButtonPrivate被寫到了QObject中的d_ptr中

            QObject::QObject(QObjectPrivate &dd, QObject *parent)
                : d_ptr(&dd)
            {
                Q_D(QObject);
                ::qt_addObject(d_ptr->q_ptr = this);
                QThread *currentThread = QThread::currentThread();
                d->thread = currentThread ? QThreadData::get(currentThread)->id : -1;
                Q_ASSERT_X(!parent || parent->d_func()->thread == d->thread, "QObject::QObject()",
                           "Cannot create children for a parent that is in a different thread.");
                if (parent && parent->d_func()->thread != d->thread)
                    parent = 0;
                if (d->isWidget) {
                    if (parent) {
                        d->parent = parent;
                        d->parent->d_func()->children.append(this);
                    }
                    // no events sent here, this is done at the end of the QWidget constructor
                } else {
                    setParent(parent);
                }
            }
            然后執(zhí)行QObject的構(gòu)造函數(shù),這里主要是一些線程的處理,先不理它

            QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)
                : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice()
            {
                d_func()->init((parent && parent->windowType() == Qt::Desktop ? parent : 0), f);
            }
            然后是QWidget的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QWidgetPrivate的init函數(shù),這個函數(shù)不是虛函數(shù),因此靜態(tài)解析成QWidgetPrivate的init函數(shù)調(diào)用

            QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
                : QWidget(dd, parent, 0)
            {
                Q_D(QAbstractButton);
                d->init();
            }
            然后是QAbstractButton的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QAbstractButton的init函數(shù),這個函數(shù)不是虛函數(shù),因此靜態(tài)解析成QAbstractButton的init函數(shù)調(diào)用

            QPushButton::QPushButton(const QString &text, QWidget *parent)
                : QAbstractButton(*new QPushButtonPrivate, parent)
            {
                Q_D(QPushButton);
                d->init();
                setText(text);
            }
            然后是QPushButton的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QPushButton的init函數(shù),這個函數(shù)不是虛函數(shù),因此靜態(tài)解析成QPushButton的init函數(shù)調(diào)用

            現(xiàn)在的事情很清楚了,總結(jié)一下:
            QPushButton在構(gòu)造的時候同時生成了QPushButtonPrivate指針,QPushButtonPrivate創(chuàng)建時依次調(diào)用數(shù)據(jù)類基類的構(gòu)造函數(shù)
            QPushButton的構(gòu)造函數(shù)中顯示的調(diào)用了基類的構(gòu)造函數(shù)并把QPushButtonPrivate指針傳遞過去,QPushButton創(chuàng)建時依次調(diào)用接口類基類的構(gòu)造函數(shù)
            在接口類的構(gòu)造函數(shù)中調(diào)用了平行數(shù)據(jù)類的init函數(shù),因為這個函數(shù)不是虛函數(shù),因此就就是此次調(diào)用了數(shù)據(jù)類的init函數(shù)

            需要指出的是,為什么QPushButtonPrivate實體類指針要轉(zhuǎn)換為引用呢?為什么不是直接傳遞指針?結(jié)論是人家喜歡這樣寫,就是不傳指針傳引用,而且要用一個*new之類的怪異語法,
            真叫人沒有辦法,其實這里用指針是一樣的,代碼看起來也自然一些.

             delete quit;
            說完了構(gòu)造,再說說析構(gòu)

            QPushButton::~QPushButton()
            {
            }
            這里當(dāng)然會調(diào)用QPushButton的析構(gòu)函數(shù)了

             QAbstractButton::~QAbstractButton()
            {
            #ifndef QT_NO_BUTTONGROUP
                Q_D(QAbstractButton);
                if (d->group)
                    d->group->removeButton(this);
            #endif
            }
            然后是QAbstractButton的析構(gòu)函數(shù)

            QWidget::~QWidget()
            {
                Q_D(QWidget);
            ...
            }
            然后是QWidget的析構(gòu)函數(shù),這里洋洋灑灑一大堆代碼,先不管它

            QObject::~QObject()
            {
            ...
            }
            最后是QObject的析構(gòu)函數(shù),這里也是洋洋灑灑的一大堆
                Q_D(QObject);
                if (d->wasDeleted) {
            #if defined(QT_DEBUG)
                    qWarning("Double QObject deletion detected");
            #endif
                    return;
                }
                d->wasDeleted = true;
            這些沒有什么好說的,就是設(shè)一個wasDeleted的標(biāo)志,防止再被引用,對于單線程情況下,馬上就要被刪除了,還搞什么標(biāo)記啊,根本沒用,但是對于多線程情況下,這個標(biāo)記應(yīng)該是有用的

                // 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);
                    }
                }
            這里是支持QPointers的實現(xiàn)代碼,我們以后再說

                emit destroyed(this);
            Qt的一個指針刪除時要發(fā)送destroyed信號,一般情況下是沒有槽來響應(yīng)的

                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();
                }
            這里清除所有子類指針,當(dāng)然每個子類指針清除時又會清除它的所有子類,因此Qt中new出來的指針很少有顯示對應(yīng)的delete,因為只要最上面的指針被框架刪除了,
            它所連帶的所有子類都被自動刪除了

                {
                    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;
            這里要刪除相關(guān)的數(shù)據(jù)類指針了

            posted on 2009-05-02 15:42 逛奔的蝸牛 閱讀(637) 評論(0)  編輯 收藏 引用 所屬分類: Qt
            99re这里只有精品热久久| 久久无码人妻精品一区二区三区| 日本福利片国产午夜久久| 亚洲第一极品精品无码久久| 精品久久人人妻人人做精品| 久久91综合国产91久久精品| 久久精品亚洲一区二区三区浴池 | 国内精品人妻无码久久久影院导航| 国产一区二区精品久久凹凸| 久久这里只精品国产99热| 久久久国产精品网站| 久久www免费人成精品香蕉| 精品久久久久久无码人妻热| 久久久久亚洲AV无码专区桃色| 久久强奷乱码老熟女| 中文字幕久久精品 | 99久久国产亚洲高清观看2024| 91精品国产综合久久香蕉| 久久狠狠一本精品综合网| 久久久久久免费视频| 99久久99久久精品免费看蜜桃| 久久夜色tv网站| 2021国产精品午夜久久| 久久久久久夜精品精品免费啦| 日本免费久久久久久久网站| 精品久久久久中文字| 国产aⅴ激情无码久久| 久久国产精品久久久| 亚洲国产精品无码久久九九| 色偷偷偷久久伊人大杳蕉| 国产精品欧美亚洲韩国日本久久 | 蜜桃麻豆www久久国产精品| 日韩人妻无码精品久久久不卡| 国产农村妇女毛片精品久久| 欧美日韩精品久久久久| 中文精品久久久久国产网址| 久久精品极品盛宴观看| a级成人毛片久久| 一本大道久久a久久精品综合| 亚洲AV伊人久久青青草原| 精品久久久久久久久中文字幕|