Qt源码初窥
今天,在给同学讲东西的时候,谈到了Qt源代码的问题,才发现自己对Qt机制的了解是在太少了,而Qt的魅力也在于它的开源。因此,决定,从今天起,每天坚持进行1小时以上的源码分析,无论如何,不能间断。
看到那无数的工程,从什么地方开始呢?想想看,也就是从自己写的程序的运行机制作为入口点吧,希望可以窥探到一些Qt的架构知识。
所有的Qt GUI程序都是从QApplication开始的,那么我们就从QApplication的构造函数开始吧。
最初的一个基于MainWindow的GUI应用程序是这样的:
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
从头文件#include <QtGui/QApplication>可以看出来,程序时从QtGui工程中开始的,让我们来一看究竟喽。
找到了QApplication的真实路径:
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这些数据结构,以及QCoreApplication(这个会有些什么内容呢,比较好奇)。
这里猜测qwindowdefs.h文件应该是用于存放全局定义的,qcursor.h这个比较明显,就是光标。
后面还有一些比较奇怪的宏定义:
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
这两个宏的定义是空的,不知道有什么用,有待以后考究,暂时认为是为了做标识吧。
QT_MODULE(Gui)
这个会是什么意思呢?等待以后研究了……
下面是一些前向声明:
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;现在不会用……以后研究,看样子Qt的源码真的非常复杂哦。
看下接下来的部分:
#if defined(qApp)
#undef qApp
#endif
#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))
这里将qApp宏定义为一个QApplication类型的指针。在此猜测,QCoreApplication的设计采用了单例设计模式。
终于看到类定义了:
class Q_GUI_EXPORT QApplication : public QCoreApplication
原来QApplication是QCoreApplication的子类哦,怪不得要做类型转换,但是这样的转换安全吗?有待考证。
Q_OBJECT
这个宏定义了元对象系统的支持,替换了如下代码:
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:
/* tmake ignore Q_OBJECT */
对于这些代码的详细分析,以后进行。代码才qobjectdefs.h中。
下面是一些属性的定义,也是利用了元对象系统:
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)
详细的分析,以后进行,我们今天得主要目的是探究构造函数是如何运行的。
看到了如下的枚举类型,不知道有何用意,以后详细研究。
public:
enum Type { Tty, GuiClient, GuiServer };
终于 看到构造函数了:
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);
通常情况下,都忽略了还有版本信息这样一个参数,会有什么用呢?……
先不去看下面的类定义了,需要什么再看,要不然,光类定义都搞不定了。
现在深入到构造函数当中看个究竟:
首先是文档内容:
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.
警告中提到了传递参数的生存期问题,由此可以知道,Qt并不负责保存命令行参数的数据,而是简单的保留了对象的指针。
The global /c qApp pointer refers to this application object. Only one
application object should be created.
看来之前的猜测没有错误,Qt在QCoreApplication的创建上采用了单例模式。
This application object must be constructed before any /l{QPaintDevice}
{paint devices} (including widgets, pixmaps, bitmaps etc.).
现在只能先注意这个问题,等以后探究其原因。
/note /a argc and /a argv might be changed as Qt removes command line
arguments that it recognizes.
再下面的文档是Qt的Debug选项
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会有很详细的解释。
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;}
终于看到构造函数了,不过时间都已经过去一个多小时……可以好好研究下了。
QApplication::QApplication(int &argc, char **argv)
: QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
{ Q_D(QApplication); d->construct(); }
不能理解的是,这个构造函数能被调用到吗?
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还有其他的一些构造函数:
#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
经过追踪之后,发现程序的构造顺序是这样的:
QObjectData->QObjectPrivate->QCoreApplicationPrivate->QApplicationPrivate->QObjectPrivate->QObject->QCoreApplication->QApplication
时间差不多了,接下来的明天继续分析,休息了。
2009年10月12日星期一 00:01