QApplication与QCoreApplication

来源:互联网 发布:数据加密标准des 编辑:程序博客网 时间:2024/04/30 14:36

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中说的只允许创建一个是指在一个函数内,只能创建一个,这里显然不是,单例的实现一般都是通过私有构造函数来实现的,这里的构造函数是共有的显然不是单例的节奏。

0 0
原创粉丝点击