日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

qt源码分析

發(fā)布時(shí)間:2025/3/21 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 qt源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
今天,在給同學(xué)講東西的時(shí)候,談到了Qt源代碼的問題,才發(fā)現(xiàn)自己對Qt機(jī)制的了解是在太少了,而Qt的魅力也在于它的開源。因此,決定,從今天起,每天堅(jiān)持進(jìn)行1小時(shí)以上的源碼分析,無論如何,不能間斷。 看到那無數(shù)的工程,從什么地方開始呢?想想看,也就是從自己寫的程序的運(yùn)行機(jī)制作為入口點(diǎn)吧,希望可以窺探到一些Qt的架構(gòu)知識。 所有的Qt GUI程序都是從QApplication開始的,那么我們就從QApplication的構(gòu)造函數(shù)開始吧。 最初的一個(gè)基于MainWindow的GUI應(yīng)用程序是這樣的: QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); 從頭文件#include <QtGui/QApplication>可以看出來,程序時(shí)從QtGui工程中開始的,讓我們來一看究竟嘍。 找到了QApplication的真實(shí)路徑: gui/kernel/qapplication.h 這里是頭文件: #include <QtCore/qcoreapplication.h> #include <QtGui/qwindowdefs.h> #include <QtCore/qpoint.h> #include <QtCore/qsize.h> #include <QtGui/qcursor.h> 可以看出來,該類使用了來自QtCore中的一些程序。QPoint,QSize這些數(shù)據(jù)結(jié)構(gòu),以及QCoreApplication(這個(gè)會有些什么內(nèi)容呢,比較好奇)。 這里猜測qwindowdefs.h文件應(yīng)該是用于存放全局定義的,qcursor.h這個(gè)比較明顯,就是光標(biāo)。 后面還有一些比較奇怪的宏定義: QT_BEGIN_HEADER QT_BEGIN_NAMESPACE 這兩個(gè)宏的定義是空的,不知道有什么用,有待以后考究,暫時(shí)認(rèn)為是為了做標(biāo)識吧。 QT_MODULE(Gui) 這個(gè)會是什么意思呢?等待以后研究了…… 下面是一些前向聲明: class QSessionManager; class QDesktopWidget; class QStyle; class QEventLoop; class QIcon; class QInputContext; template <typename T> class QList; class QLocale; #if defined(Q_WS_QWS) class QDecoration; #endif class QApplication; class QApplicationPrivate; 模板類的前向聲明還是頭一次見到:template <typename T> class QList;現(xiàn)在不會用……以后研究,看樣子Qt的源碼真的非常復(fù)雜哦。 看下接下來的部分: #if defined(qApp) #undef qApp #endif #define qApp (static_cast<QApplication *>(QCoreApplication::instance())) 這里將qApp宏定義為一個(gè)QApplication類型的指針。在此猜測,QCoreApplication的設(shè)計(jì)采用了單例設(shè)計(jì)模式。 終于看到類定義了: class Q_GUI_EXPORT QApplication : public QCoreApplication 原來QApplication是QCoreApplication的子類哦,怪不得要做類型轉(zhuǎn)換,但是這樣的轉(zhuǎn)換安全嗎?有待考證。 Q_OBJECT 這個(gè)宏定義了元對象系統(tǒng)的支持,替換了如下代碼: public: \ Q_OBJECT_CHECK \ static const QMetaObject staticMetaObject; \ virtual const QMetaObject *metaObject() const; \ virtual void *qt_metacast(const char *); \ QT_TR_FUNCTIONS \ virtual int qt_metacall(QMetaObject::Call, int, void **); \ private: 對于這些代碼的詳細(xì)分析,以后進(jìn)行。代碼才qobjectdefs.h中。 下面是一些屬性的定義,也是利用了元對象系統(tǒng): Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection) Q_PROPERTY(QIcon windowIcon READ windowIcon WRITE setWindowIcon) Q_PROPERTY(int cursorFlashTime READ cursorFlashTime WRITE setCursorFlashTime) Q_PROPERTY(int doubleClickInterval READ doubleClickInterval WRITE setDoubleClickInterval) Q_PROPERTY(int keyboardInputInterval READ keyboardInputInterval WRITE setKeyboardInputInterval) #ifndef QT_NO_WHEELEVENT Q_PROPERTY(int wheelScrollLines READ wheelScrollLines WRITE setWheelScrollLines) #endif Q_PROPERTY(QSize globalStrut READ globalStrut WRITE setGlobalStrut) Q_PROPERTY(int startDragTime READ startDragTime WRITE setStartDragTime) Q_PROPERTY(int startDragDistance READ startDragDistance WRITE setStartDragDistance) Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed) #ifndef QT_NO_STYLE_STYLESHEET Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet) 詳細(xì)的分析,以后進(jìn)行,我們今天得主要目的是探究構(gòu)造函數(shù)是如何運(yùn)行的。 看到了如下的枚舉類型,不知道有何用意,以后詳細(xì)研究。 public: enum Type { Tty, GuiClient, GuiServer }; 終于 看到構(gòu)造函數(shù)了: QApplication(int &argc, char **argv, int = QT_VERSION); QApplication(int &argc, char **argv, bool GUIenabled, int = QT_VERSION); QApplication(int &argc, char **argv, Type, int = QT_VERSION); 通常情況下,都忽略了還有版本信息這樣一個(gè)參數(shù),會有什么用呢?…… 先不去看下面的類定義了,需要什么再看,要不然,光類定義都搞不定了。 現(xiàn)在深入到構(gòu)造函數(shù)當(dāng)中看個(gè)究竟: 首先是文檔內(nèi)容: Initializes the window system and constructs an application object with \a argc command line arguments in \a argv. \warning The data referred to by \a argc and \a argv must stay valid for the entire lifetime of the QApplication object. In addition, \a argc must be greater than zero and \a argv must contain at least one valid character string. 警告中提到了傳遞參數(shù)的生存期問題,由此可以知道,Qt并不負(fù)責(zé)保存命令行參數(shù)的數(shù)據(jù),而是簡單的保留了對象的指針。 The global \c qApp pointer refers to this application object. Only one application object should be created. 看來之前的猜測沒有錯誤,Qt在QCoreApplication的創(chuàng)建上采用了單例模式。 This application object must be constructed before any \l{QPaintDevice} {paint devices} (including widgets, pixmaps, bitmaps etc.). 現(xiàn)在只能先注意這個(gè)問題,等以后探究其原因。 \note \a argc and \a argv might be changed as Qt removes command line arguments that it recognizes. 再下面的文檔是Qt的Debug選項(xiàng) Qt debugging options (not available if Qt was compiled without the QT_DEBUG flag defined): \list \o -nograb, tells Qt that it must never grab the mouse or the keyboard. \o -dograb (only under X11), running under a debugger can cause an implicit -nograb, use -dograb to override. \o -sync (only under X11), switches to synchronous mode for debugging. \endlist See \l{Debugging Techniques} for a more detailed explanation. 在文檔中查找Debugging Techniques會有很詳細(xì)的解釋。 QApplication::QApplication(int &argc, char **argv) : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) { Q_D(QApplication); d->construct(); } QApplication::QApplication(int &argc, char **argv, int _internal) : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) { Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;} 終于看到構(gòu)造函數(shù)了,不過時(shí)間都已經(jīng)過去一個(gè)多小時(shí)……可以好好研究下了。 QApplication::QApplication(int &argc, char **argv) : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) { Q_D(QApplication); d->construct(); } 不能理解的是,這個(gè)構(gòu)造函數(shù)能被調(diào)用到嗎? QApplication(int &argc, char **argv, int = QT_VERSION); QApplication(int &argc, char **argv, bool GUIenabled, int = QT_VERSION); QApplication(int &argc, char **argv, Type, int = QT_VERSION); 聲明是上面的樣子。去測試一下。 原來Qt還有其他的一些構(gòu)造函數(shù): #if defined(Q_INTERNAL_QAPP_SRC) || defined(qdoc) QApplication(int &argc, char **argv); QApplication(int &argc, char **argv, bool GUIenabled); QApplication(int &argc, char **argv, Type); #if defined(Q_WS_X11) QApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0); QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); #endif #endif 經(jīng)過追蹤之后,發(fā)現(xiàn)程序的構(gòu)造順序是這樣的: QObjectData->QObjectPrivate->QCoreApplicationPrivate->QApplicationPrivate->QObjectPrivate->QObject->QCoreApplication->QApplication 首先,我們從最開始的QObjectData類進(jìn)行研究: 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 inEventHandler : 1; uint inThreadChangeEvent : 1; uint unused : 23; int postedEvents; }; 以上是整個(gè)類的實(shí)現(xiàn),我們發(fā)現(xiàn),該類只有數(shù)據(jù)成員,也就是一個(gè)純的數(shù)據(jù)封裝。虛的析構(gòu)函數(shù)說明,該類將被其他類所繼承。同時(shí),通過資料,我了解到,Qt在此的設(shè)計(jì)模式采用了句柄實(shí)體模式,也就是以QObject為基類的類一般都是句柄類,一般只有一個(gè)指針指向一個(gè)實(shí)體類,在實(shí)體類中保存全部的數(shù)據(jù)。這樣做,第一是將數(shù)據(jù)與實(shí)現(xiàn)分離,方便了以后修改,同時(shí)使得函數(shù)傳遞對象的速度變得很快,而不需要傳遞不安全的指針。 另外一個(gè)問題是我看到了一個(gè)以前一直沒見過的語法現(xiàn)象:uint isWidget : 1;經(jīng)過查資料,發(fā)現(xiàn),該語法現(xiàn)象稱作位域,位域的產(chǎn)生是為了節(jié)省空間,是一個(gè)C語言的語法規(guī)則。在變量定義后的數(shù)字表示了該變量只會使用1字節(jié),編譯器可以對其存儲結(jié)構(gòu)進(jìn)行優(yōu)化。 QObject *q_ptr; QObject *parent; QObjectList children; 后面兩個(gè)應(yīng)該是分別保存了父類指針,和子類對象指針,形成一個(gè)樹形的結(jié)構(gòu)。 由于該類沒有構(gòu)造函數(shù),因此,該類就分析到這里。接下來看其子類的構(gòu)造方法。 QObjectPrivate 該類在qobject_p.h中,構(gòu)造方法比較簡單,初始化了一些屬性 QObjectPrivate::QObjectPrivate(int version) : threadData(0), currentSender(0), currentChildBeingDeleted(0), connectionLists(0) { if (version != QObjectPrivateVersion) qFatal("Cannot mix incompatible Qt libraries"); // QObjectData initialization q_ptr = 0; parent = 0; // no parent yet. It is set by setParent() isWidget = false; // assume not a widget object pendTimer = false; // no timers yet blockSig = false; // not blocking signals wasDeleted = false; // double-delete catcher sendChildEvents = true; // if we should send ChildInsert and ChildRemove events to parent receiveChildEvents = true; postedEvents = 0; extraData = 0; connectedSignals = 0; inEventHandler = false; inThreadChangeEvent = false; deleteWatch = 0; } 在整個(gè)類的傳遞過程中,我們一直可以看到一個(gè)Qt版本的宏定義被提供,在這里可以看到,當(dāng)版本不一致時(shí),會導(dǎo)致嚴(yán)重的警告,并且運(yùn)行會失敗。 同時(shí)我們也看到了,Qt在對這些屬性賦值的時(shí)候,確實(shí)只用到了0、1兩個(gè)數(shù)值。 就這樣,這個(gè)類的構(gòu)造函數(shù)看完了,下面是QCoreApplicationPrivate類了。該類構(gòu)造函數(shù),將系統(tǒng)傳遞的命令行參數(shù)接收了。 QCoreApplicationPrivate(int &aargc, char **aargv) 可以看到,在類的定義中 int &argc; char **argv; 我們看到了命令行參數(shù)的引用與指針,也就是說,Qt是不負(fù)責(zé)維護(hù)命令行參數(shù)的數(shù)據(jù)的。這也是昨天為什么會看到其文檔中會提到保證命令行數(shù)據(jù)始終有效。 static const char *const empty = ""; if (argc == 0 || argv == 0) { argc = 0; argv = (char **)&empty; // ouch! careful with QCoreApplication::argv()! } 這里,Qt在命令行參數(shù)為空時(shí)做了賦值,同時(shí)將argv的指針指向了一個(gè)空字符串。這樣是為了安全嗎?不是很理解…… #ifdef Q_OS_UNIX qt_application_thread_id = QThread::currentThreadId(); #endif 在這里,我們看到了,如果是在UNIX系統(tǒng)中時(shí),將保存當(dāng)前線程ID,具體有什么用意呢?或許以后會知道的…… 看到一句提示: // note: this call to QThread::currentThread() may end up setting theMainThread! 具體含義,可能是說,該處調(diào)用currentThread可能會導(dǎo)致主線程終止。真正含義及原因,有待考證。 if (QThread::currentThread() != theMainThread) qWarning("WARNING: QApplication was not created in the main() thread."); 這里判斷界面應(yīng)用程序是否在主線程中創(chuàng)建,Qt目前是不支持在其他線程中進(jìn)行界面類操作的。 今天的起點(diǎn)是QObject的構(gòu)造函數(shù)。 Q_INVOKABLE explicit QObject(QObject *parent=0); 構(gòu)造函數(shù)的聲明是這樣的,首先看到的是宏定義:Q_INVOKABLE,該宏在Qt文檔中有說明,用于將函數(shù)在元對象系統(tǒng)中注冊。Explicit關(guān)鍵字,該關(guān)鍵字可以阻止不應(yīng)該允許的經(jīng)過轉(zhuǎn)換構(gòu)造函數(shù)進(jìn)行的隱式轉(zhuǎn)換的發(fā)生。聲明為explicit的構(gòu)造函數(shù)不能在隱式轉(zhuǎn)換中使用。 接下來是實(shí)現(xiàn)的函數(shù)頭: QObject::QObject(QObject *parent) : d_ptr(new QObjectPrivate) 可以看出來,該類創(chuàng)建了QObjectPrivate的對象,這樣使得經(jīng)常修改的內(nèi)部操作與標(biāo)準(zhǔn)接口分離,同時(shí)還為QObject類做了減肥。 Q_D(QObject); qt_addObject(d_ptr->q_ptr = this); d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); d->threadData->ref(); if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) parent = 0; setParent(parent); 首先看到的是Q_D宏,該宏的實(shí)現(xiàn)是這樣的: #define Q_D(Class) Class##Private * const d = d_func() 傳入?yún)?shù)是QObject,將宏展開后得到: QObjectPrivate * const d = d_func(); 這里的d_func()有幾種不同的實(shí)現(xiàn): #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_PRIVATE_D(Dptr, Class) \ inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } \ inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } \ friend class Class##Private; 暫時(shí)還不清楚到底是哪個(gè),繼續(xù)往下看了。 qt_addObject(d_ptr->q_ptr = this); 這個(gè)函數(shù)中,首先是d_ptr->q_ptr = this,該方法將當(dāng)前對象的指針賦值給了剛剛創(chuàng)建的QObjectPrivate類中的指針。接下來看這個(gè)函數(shù): qt_addObject() 沒有找到具體的函數(shù)實(shí)現(xiàn),只是發(fā)現(xiàn)了采用C函數(shù)的聲明。 extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) 接下來的做法是獲取線程ID了 d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); d->threadData->ref(); if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) parent = 0; 詳細(xì)實(shí)現(xiàn)在分析QThreadData時(shí)再做了解。 setParent(parent); 這句話設(shè)置父對象的指針,為了方便引用。開始感覺到,Qt的實(shí)現(xiàn)機(jī)制中,對靈活性的追求要勝于對速度的追求。 接下來的構(gòu)造函數(shù)是QCoreApplication類的了,終于要接觸到最后兩個(gè)構(gòu)造函數(shù)了。 簡單看下文檔說明,QCoreApplication類提供了一個(gè)事件循環(huán),用于提供對控制臺Qt應(yīng)用程序的支持。 這個(gè)類采用了無GUI的事件循環(huán),所有的操作系統(tǒng)事件都將進(jìn)入到主循環(huán)當(dāng)中。負(fù)責(zé)對其他來源的處理,并派遣。同時(shí)該類負(fù)責(zé)了對象的初始化以及結(jié)束。程序的時(shí)間循環(huán)在調(diào)用exec()函數(shù)時(shí)開始。長時(shí)間的操作可以通過調(diào)用processEvents()來保證應(yīng)用程序的響應(yīng)。 做了下測試,在一個(gè)死循環(huán)中調(diào)用了該函數(shù),發(fā)現(xiàn)程序又可以正常響應(yīng)了。 得到另外一個(gè)說明是:exit()函數(shù)需要在所有事件循環(huán)退出后才會返回。 言歸正傳,現(xiàn)在看其構(gòu)造函數(shù),發(fā)現(xiàn)構(gòu)造了對象QObject(p, 0),之后調(diào)用了init函數(shù)。看到一個(gè)標(biāo)記,說子類需要調(diào)用QCoreApplicationPrivate::eventDispatcher->startingUp(); 函數(shù)。現(xiàn)在還不清楚是什么含義。 跟蹤到init()函數(shù),發(fā)現(xiàn)這里做的事情還真不少。 Q_D(QCoreApplication); 首先創(chuàng)建了QCoreApplicationPrivate的一個(gè)指針。下面是針對不同平臺的一些定義。 在Windows平臺下,調(diào)用了set_winapp_name()函數(shù)。又是一個(gè)外部定義的函數(shù),暫時(shí)找不到實(shí)現(xiàn),所以不能繼續(xù)了。但是看到一個(gè)提示,當(dāng)qWinMain()函數(shù)無效的時(shí)候,用于獲取應(yīng)用程序名稱和實(shí)例。 再往下是Q_ASSERT_X宏定義,該宏定義用于打印斷言信息。 ASSERT failure in divide: "division by zero", file mainwindow.cpp, line 19。

總結(jié)

以上是生活随笔為你收集整理的qt源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。