QApplication與QCoreApplication

QApplication (GUI 程序中 有且僅有一個)

QApplication 類 管理GUI程序的控制流和主設置。

QApplication 包含主事件循環。所有來自窗口系統和其他源的事件將被處理和分配。它也處理程序的初始化,析構和提供會話管理。

對於非GUI的用QCoreApplication 代替QApplication,它不依賴QtGui庫。

qApp是一個全局的指針,指向QApplication的對象。

QApplication的主要職責如下:

1,初始化程序的用戶桌面設置,如palette(),font(),doubleClickInterval()(鼠標雙擊的時間間隔),並一直監視這些屬性,以防用戶改變他們(得到及時的更新)。

2,處理事件,意思是它接收來自底層窗口系統的事件,並把他們分發給關聯的窗口,通過sendEvent(),postEvent(),你可以把你自己的事件發給部件。

3,解析命令行參數。

4,定義程序的觀感(被封裝在QStyle 對象中)。通過setStyle()可以實時的改變。

5,它知道程序的窗口信息。可以通過widgetAt(),還可以得到一個窗口列表通過topLevelWidgets(),然後通過closeAllWindows()關閉所有窗口。

6,還管理鼠標操作。

7,它還提供一個複雜的會話管理。它使程序在用戶退出時可以“優美”的結束,或者如果幹掉一個進程如果這個進程不能保留程序之前的狀態(對會話管理不瞭解,翻譯的不準確)

由於QApplication對象做了這麼多初始化操作,所以它必須在所以與用戶接口有關的對象創建之前被創建。

QCoreApplication類exec()方法分析

看看這個類是怎麼調用線程那些東西的。
在QtCoreApplication類的exec()方法:

if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;

    QThreadData *threadData = self->d_func()->threadData;
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }

    threadData->quitNow = false;
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
    if (self) {
        self->d_func()->in_exec = false;
        if (!self->d_func()->aboutToQuitEmitted)
            emit self->aboutToQuit();
        self->d_func()->aboutToQuitEmitted = true;
        sendPostedEvents(0, QEvent::DeferredDelete);
    }

    return returnCode;

我重點想知道的是,d_func()函數如何得到QThreadData對象的,這個需要進入到QCoreApplicationPrivate類當中了。
發現了這樣的定義:

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

原來該類調用了qGetPtrHelper方法,繼續跟蹤下去:

template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }

其實d_func()是將QObjectData類的指針轉換爲QCoreApplicatPrivate類的指針。看下繼承關係,QCoreApplicatPrivate繼承自QObjectPrivate,QObjectPrivate繼承自QObjectData;但是這樣的轉換還是會帶來危險,因爲本來這個指針就不是該類的,這樣相當於擴大了對象的引用內存的大小,容易產生越界錯誤。而且這樣的強轉之後,也只是調用了其中來自 QObjectData的方法,這裏的用意不是很明白,應該在其他地方對應用指針做了新的賦值。去找找看。
QScopedPointer d_ptr的定義來自QObject,現在找找看,是否有對該指針的賦值:
QObject::QObject(QObject *parent): d_ptr(new QObjectPrivate)
實際的引用對象就是QObjectPrivate類的對象了。
現在就可以總結一下了,整個Qt控制檯應用程序框架基本明瞭:
我們編寫的代碼是:

QCoreApplication a(argc,argv);
a.exec();

通過這兩行代碼,Qt爲我們做了如下工作:
首先創建一個用於保存整個程序運行核心數據的類對象:
QCoreApplication繼承自QObject,所以,首先構造QOjbect類對象,於是有了:
D_ptr(new QObjectPrivate)
隨後對QThread進行初始化:

#ifndef QT_NO_THREAD
    QThread::initialize();
#endif

接下來創建事件派送器,從Windows中將消息傳遞至Qt,並進行分發,構造函數完成。
隨後調用a.exec()方法,獲取線程信息:
QThreadData *threadData = self->d_func()->threadData;
最後進入事件循環,並進行事件發送等

對exec()函數的後半部分進行分析:

QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
    if (self) {
        self->d_func()->in_exec = false;
        if (!self->d_func()->aboutToQuitEmitted)
            emit self->aboutToQuit();
        self->d_func()->aboutToQuitEmitted = true;
        sendPostedEvents(0, QEvent::DeferredDelete);
    }

該方法在程序退出之前是不返回的,eventLoop.exec()的調用會讓程序進入一個事件循環,直到程序結束。
進入QEventLoop中的exec方法:

Q_D(QEventLoop);
    if (d->threadData->quitNow)
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
    d->inExec = true;
    d->exit = false;
    ++d->threadData->loopLevel;
    d->threadData->eventLoops.push(this);
首先將事件類對象壓入線程結構體中。
// remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);

英文的註釋說的已經很清楚了。

#if defined(QT_NO_EXCEPTIONS)
    while (!d->exit)
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
#else
    try {
        while (!d->exit)
            processEvents(flags | WaitForMoreEvents | EventLoopExec);
    } catch (...) {
        qWarning("Qt has caught an exception thrown from an event handler. Throwing/n"
                 "exceptions from an event handler is not supported in Qt. You must/n"
                 "reimplement QApplication::notify() and catch all exceptions there./n");

        // copied from below
        QEventLoop *eventLoop = d->threadData->eventLoops.pop();
        Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
        Q_UNUSED(eventLoop); // --release warning
        d->inExec = false;
        --d->threadData->loopLevel;

        throw;
    }
#endif

在此調用事件循環不同平臺下的事件分派器的processEvent方法進行事件分析。
當出現異常時,將當前事件放棄掉。

// copied above
    QEventLoop *eventLoop = d->threadData->eventLoops.pop();
    Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
    Q_UNUSED(eventLoop); // --release warning
    d->inExec = false;
    --d->threadData->loopLevel;

    return d->returnCode;

在此,事件循環退出。

qcoreapplication 與qapplication的聯繫與區別

故事的背景是這樣的,我們在寫QT程序的時候或者在開始寫QT程序之前總會看到這樣的語句

QApplication app(argc, argv);
這是什麼呢? QApplication這個類是繼承QCoreApplication的,而QCoreApplication有繼承

QObject的,而QObject就是QT中最基本的基類,也就是QT的根基了,這裏就從QCoreApplication

說起吧,頭文件中有這樣的開始

classQ_CORE_EXPORTQCoreApplication : public QObject
Q_CORE_EXPORT是什麼呢?如果在編寫動態庫時,定義DLL符號,Q_GUI_EXPORT就是導出函數或者類了,如果在應用程序中使用時,不定義Dll符號,Q_GUI_EXPORT就是導入類或者函數了,這裏當然是導入了,我們寫的可是命令行的,不是編寫動態庫,下面就是一些函數和變量的定義了,看到這裏我震驚了

QCoreApplication * instance ()
定義一個指向自己的實例,啊?這是要鬧哪樣啊?難道要調用自己不成,實現就在類內,因爲是靜態的static QCoreApplication *instance() { returnself; }就是返回一個 self

static QCoreApplication *self;
是一個私有的靜態成員變量,實現在類外

QCoreApplication *QCoreApplication::self = 0;
這下算是知道這個 self是做什麼的了,可是這裏有個疑問,連QCoreApplication自己都沒被創建呢,哪來的指向 QCoreApplication的指針存在呢?這裏個人的解釋是靜態成員的創建應該類創建之前就已經存在了,至於這個指向暫且就不考慮了,或許系統會解決這個問題吧,這裏就當成是一個指針吧。

這裏有必要看一下這個QCoreApplication類的構造函數

QCoreApplication::QCoreApplication(int &argc, char **argv)     : QObject(*new QCoreApplicationPrivate(argc, argv)) 
{     
    init();
    QCoreApplicationPrivate::eventDispatcher->startingUp(); 
    #if defined(Q_OS_SYMBIAN) && !defined(QT_NO_LIBRARY)// Refresh factoryloader, as text codecs are requested during lib path// resolving process and won't be therefore properly loaded.// Unknown if this is symbian specific issue.     
    QFactoryLoader::refreshAll(); 
    #endif#if defined(Q_OS_SYMBIAN) &&!defined(QT_NO_SYSTEMLOCALE)     
    d_func()->symbianInit(); 
#endif 
}

下面看着有點暈了,這裏就只看 init();這個函數,就在構造函數的下面,這裏的代碼更多了,

這裏只寫我們關注的一行

voidQCoreApplication::init(){ QCoreApplication::self = this;}
這裏對 self進行了賦值,這就讓這個self指向了當前這個對象,而不是什麼也不指了,這個self就可以代替當前對象來使用了,當然這只能在類內,因爲self是私有的,要在類外使用是不是應該定義一個共有成員函數什麼的,先把疑問留在這裏。

說完這個靜態成員變量self還是沒有解決這裏爲什麼要鬧這樣的問題,原來我們在類定義的上一行看到這樣的代碼

#define qApp QCoreApplication::instance()

定義了一個 qApp宏,這個宏也就成了一個指針,指向的是自己,這樣做又有什麼用呢

當我們在主程序中定義了 QCoreApplication app(argc, argv);對象的時候完全是不需要用qApp這個宏的啊,但是如果出了主函數要用這個對象怎麼辦,傳嗎?這樣比較麻煩,QT用這個指向自己的東東就是幫助我們解決這樣要在主函數外使用app這個對象的而找不到對象的苦惱。好了,在函數外你就用qApp吧,這樣會不會有什麼問題呢?這個東西在內存嗎?嘿嘿,這是靜態的啊,就在內存呆着呢,大膽的去用這個指向自己的宏指針吧,只要app沒析構這東東就一直在內存。

下面來說明一下QApplication,這個是從 QCoreApplication繼承來的,

#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))

在類定義前有一段這樣的代碼,這裏也是取 self這個指向自己的指針但是要做一個類型轉換,至於這個類型轉換是否安全,我想應該是安全的,因爲相當於是從基類往派生類轉換,基類有的應該是對拷貝的,但是這裏的static會不會對這個造成困擾還不是很清楚,總之,要轉換後這個qApp才能在主函數外使用。

這裏還要說明的是這個 QCoreApplication是不是單例的問題,網上有很多人認爲是單例,也有很多人贊成,但是我實踐了一下應該不是單例

for(int i = 0 ; i < 3 ; ++i) 
{    
     QCoreApplication app;
  }

這裏這樣的操作時允許的,因爲之前的已經析構了,QT中說的只允許創建一個是指在一個函數內,只能創建一個,這裏顯然不是,單例的實現一般都是通過私有構造函數來實現的,這裏的構造函數是共有的顯然不是單例的節奏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章