青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

逛奔的蝸牛

我不聰明,但我會很努力

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

轉自: 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,除了虛函數表指針需要的4個字節(jié)以外,另外的4個字節(jié)是:
    QObjectData *d_ptr;
QObject中的數據被封裝在QObjectData類中了,為什么要封裝數據呢?
原因是Qt中有一個很重要的設計模式就是句柄實體模式,也就是以QObject為基類的類一般都是句柄類,一般只有一個指針指向一個實體類,在實體類中保存全部的數據
而且一般情況下這個指針還是私有的,方便以后修改句柄類的實現細節(jié)
因此,也可以說和句柄類繼承關系平行的也有一套實體類派生體系,因此,準確的說,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;
這個指針指向實體類對應的句柄類,這和上面的代碼
QObjectData *d_ptr;
遙相呼應,使得句柄類和實體類可以雙向的引用,為什么是這樣的命名方式呢?可能q指的是Qt接口類,d指的是Data數據類,這當然是猜測了,但是或許可以方便你記憶,在Qt中,
這兩個指針名字是非常重要的,必須記住

但是僅僅如此還是不容易使用這兩個指針,因為它們都是基類的類型,難道每次使用都要類型轉換嗎?為了簡單起見,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;
只要在類的頭文件中使用這兩個宏,就可以通過函數直接得到實體類和句柄類的實際類型了,而且這里還聲明了友元,使得數據類和句柄類連訪問權限也不用顧忌了

而且為了cpp文件中調用的方便,更是直接聲明了以下兩個宏
#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函數是非常常用的函數,可以理解為一個是得到數據類,一個是得到Qt接口類

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

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

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

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

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

QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
    : QWidget(dd, parent, 0)
QAbstractButton的構造函數中繼續(xù)調用基類QWidget的構造函數,同時把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構造時同時產生的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的構造函數,這里主要是一些線程的處理,先不理它

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的構造函數,這里調用了數據類QWidgetPrivate的init函數,這個函數不是虛函數,因此靜態(tài)解析成QWidgetPrivate的init函數調用

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

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

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

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

 delete quit;
說完了構造,再說說析構

QPushButton::~QPushButton()
{
}
這里當然會調用QPushButton的析構函數了

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

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

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

    // 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的實現代碼,我們以后再說

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

    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();
    }
這里清除所有子類指針,當然每個子類指針清除時又會清除它的所有子類,因此Qt中new出來的指針很少有顯示對應的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;
這里要刪除相關的數據類指針了

posted on 2009-05-02 15:42 逛奔的蝸牛 閱讀(648) 評論(0)  編輯 收藏 引用 所屬分類: Qt
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美大片国产精品| 国产视频久久久久久久| 欧美一区二区三区四区在线观看地址 | 久久久久久999| 国产精品99久久久久久久久久久久| 亚洲一区高清| 亚洲精品视频啊美女在线直播| 一区二区三区自拍| 欧美激情在线免费观看| 久久噜噜噜精品国产亚洲综合| 午夜精品福利一区二区蜜股av| 亚洲日本aⅴ片在线观看香蕉| 国产综合视频| 亚洲丶国产丶欧美一区二区三区| 国产亚洲精品综合一区91| 国产精品一区免费视频| 国产精品v亚洲精品v日韩精品 | 欧美一乱一性一交一视频| 亚洲综合成人在线| 亚洲在线日韩| 午夜精品久久久久久| 亚洲永久免费视频| 午夜精品久久久久久99热| 亚洲淫性视频| 久久久久久久欧美精品| 毛片基地黄久久久久久天堂| 久久另类ts人妖一区二区| 久久精品五月| 毛片基地黄久久久久久天堂| 欧美精品日韩综合在线| 欧美深夜福利| 国产三区精品| 亚洲国产精品www| 亚洲人成网站999久久久综合| 亚洲卡通欧美制服中文| 日韩亚洲综合在线| 日韩一级免费观看| 欧美亚洲专区| 美女主播精品视频一二三四| 亚洲二区三区四区| 亚洲人www| 亚洲一区欧美| 看片网站欧美日韩| 欧美日韩三级视频| 国产一级精品aaaaa看| 亚洲永久在线观看| 亚洲无线视频| 中文av一区特黄| 久久久精品国产免费观看同学| 欧美成人官网二区| 亚洲日韩欧美视频一区| 午夜精品久久| 久久影视精品| 久久久久一区二区| 国产精品盗摄久久久| 亚洲大胆女人| 欧美一区二区精品在线| 欧美国产先锋| 欧美一区免费视频| 欧美精品成人| 黄色成人av在线| 午夜精品美女久久久久av福利| 欧美成人一区二区在线| 亚洲性线免费观看视频成熟| 久久综合伊人| 好吊妞**欧美| 久久成人av少妇免费| 日韩视频在线播放| 老鸭窝91久久精品色噜噜导演| 国产欧美韩日| 一本久道久久综合狠狠爱| 99re热这里只有精品免费视频| 欧美在线观看www| 亚洲小少妇裸体bbw| 欧美精品二区| 在线播放精品| 中文网丁香综合网| 亚洲日本理论电影| 欧美专区第一页| 欧美成人中文字幕| 91久久国产综合久久91精品网站| 一区二区三区成人| 欧美一级夜夜爽| 亚洲一区欧美| 国产人久久人人人人爽| 午夜日本精品| 午夜精品一区二区三区四区 | 午夜在线视频观看日韩17c| 欧美午夜视频网站| 亚洲永久字幕| 午夜精品久久久久久久久久久久久| 免费看精品久久片| 91久久久一线二线三线品牌| 六月丁香综合| 欧美成人午夜视频| 一本综合精品| 亚洲图片你懂的| 国产精品一区在线观看你懂的| 欧美一区二区三区免费观看视频 | 久久婷婷av| 久久本道综合色狠狠五月| 国产欧美日韩另类视频免费观看 | 亚洲精品久久久一区二区三区| 久久av二区| 久久国产精品亚洲va麻豆| 国产在线高清精品| 欧美大学生性色视频| 欧美精品一区二区久久婷婷| 亚洲最新视频在线播放| 亚洲国产高清aⅴ视频| 欧美日韩三区四区| 久久精品二区| 欧美肥婆bbw| 亚洲欧美久久久| 欧美在线视频一区二区| 亚洲区欧美区| 这里只有精品丝袜| 国内免费精品永久在线视频| 另类图片综合电影| 欧美日本一道本在线视频| 午夜免费在线观看精品视频| 久久久久久久91| 亚洲午夜高清视频| 欧美在线视频一区二区三区| 亚洲国产激情| 亚洲资源av| 国产精品区一区二区三区| 乱人伦精品视频在线观看| 欧美日韩在线播放三区四区| 久久这里只精品最新地址| 欧美日韩亚洲综合一区| 久久久亚洲人| 欧美视频观看一区| 久久中文字幕一区二区三区| 欧美一区国产在线| 在线精品视频免费观看| 欧美在线视频二区| 久久黄色小说| 亚洲精品中文字幕在线| 欧美一区二区视频免费观看| av成人毛片| 免费看的黄色欧美网站| 欧美亚洲一区二区在线观看| 欧美日韩高清区| 久久夜色精品国产噜噜av| 猫咪成人在线观看| 一区二区国产日产| 欧美国产精品v| 欧美成人精品在线| 国产一区久久| 亚洲欧美日韩一区在线| 亚洲香蕉伊综合在人在线视看| 亚洲精品国产精品久久清纯直播 | 欧美成人精品在线| 亚洲啪啪91| 亚洲一区美女视频在线观看免费| 99视频在线观看一区三区| 欧美国产第二页| 亚洲看片一区| 亚洲欧美在线高清| 国产视频精品免费播放| 久久久久久久性| 欧美大片国产精品| 一本一本a久久| 国产精品视频在线观看| 羞羞视频在线观看欧美| 久久综合久久综合这里只有精品| 在线观看不卡av| 欧美日韩不卡一区| 亚洲一区二区三区精品在线| 久久亚洲精选| 一本大道久久a久久精二百| 国产精品日韩在线观看| 久久免费精品视频| 亚洲精品三级| 久久综合久久综合这里只有精品 | 永久555www成人免费| 欧美成熟视频| 亚洲女ⅴideoshd黑人| 老妇喷水一区二区三区| 一区二区高清视频| 黄色成人在线网址| 国产精品扒开腿做爽爽爽视频| 久久精品国产一区二区三| 亚洲激情网址| 久久久国产亚洲精品| 一区二区三区不卡视频在线观看 | 亚洲成人在线视频网站| 欧美色道久久88综合亚洲精品| 欧美一区三区三区高中清蜜桃| 欧美国产一区二区三区激情无套| 亚洲欧美精品一区| 在线性视频日韩欧美| 亚洲第一福利视频| 久久精品国产亚洲aⅴ| 亚洲视频免费在线观看| 欧美激情在线狂野欧美精品| 久热国产精品| 久久精品国产久精国产一老狼 | 另类专区欧美制服同性|