MFC与QT的消息机制的区别

Windows消息

Windows的消息系统由以下3部分组成:

        消息队列:Windows能够为所有的应用程序维护一个消息队列,应用程序必须从消息队列中获去消息, 然后分派给某个窗体。

        消息循环:通过这个循环机制,应用程序从消息队列中检索消息,再把它分派给适当的窗口,然后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。

        窗口过程:每个窗口都有一个窗口过程,以接收Windows 传递给窗口的消息,窗口过程的任务就是获取消息并且响应它。窗口过程是一个回调函数,处理完一个消息后,通常要给Windows一个返回值。

        Windows 系统中有两种消息队列,一种是系统消息队列,另一种是应用程序消息队列。计算机的所有输入设备由 Windows 监控,当一个事件发生时,Windows 先将输入的消息放入系统消息队列中,然后再将输入的消息拷贝到相应的应用程序队列中,应用程序中的消息循环从它的消息队列中检索每一个消息并发送给相应的窗口函数中。一个事件的发生,到达处理它的窗口函数必须经历上述过程。 

         Windows 操作系统为每个线程维持一个消息队列,当事件产生时,操作系统感知这一事件的发生,并包装成消息发送到消息队列,应用程序通过GetMessage()函数取得消息并存于一个消息结构体中,然后通过一个TranslateMessage()DispatchMessage()解释和分发消息。        

MFC消息机制

        MFC是Windows下程序设计的最流行的一个类库,MFC中的消息映射机制实质是一张巨大的消息及其处理函数对应表,MFC消息机制本质与Win相同,只是对其处理进行了封装---即消息映射机制。

其实现如下:

 

        假设ParentWnd处理了100个消息,并且将这100个处理函数声明为虚函数。

        此时有一个子类Childwnd需要处理2个消息,另外98个交由ParentWnd处理。

        但是ChildWnd的虚表仍然占有100个函数指针,其中2个指向自己的实现,另外98个指向父类的实现。

         MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。

 

        MFC是事件驱动的架构。要执行任何操作,都必须是对特定的消息作出响应。Windows对应用程序发送的信息数以千计,所以用户需要对特定的消息进行分别注册和实现。

Qt消息

        QT 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象、易于扩展、真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT 库的基础之上。QT 支持下列平台:MS/WINDOWS-9598NT 2000UNIX/X11-LinuxSun SolarisHP-UXDigital UnixIBM AIXSGI IRIXEMBEDDED- 支持 framebuffer Linux 平台。伴随着 KDE 的快速发展和普及,QT 很可能成为 Linux 窗口平台上进行软件开发时的 GUI 首选。

        而QT跨平台C++图形用户界面应用程序开发框架,Qt的消息机制是建立在SIGNAL()发送和SLOT()接受的基础上的。这个机制是对象间建立联系的核心机制。利用SIGNAL()可以传递任何的参数。他的功能非常的强大。相比MFC来说,QT的信号和槽机制功能更强大,而且容易复用。

 Qt信号和槽机制

QT的消息机制

 信号和槽

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button("button_quit");
    QObject::connect(&button, &QPushButton::clicked(), app, &QApplication::quit());
    button.show();
    return app.exec();
}

connect()函数最常用的一般形式:

connect(sender, signal, receiver, slot);

参数:

 sender:发出信号的对象

 signal:发送对象发出的信号

 receiver:接收信号的对象

 slot:接收对象在接收到信号之后所需要调用的函数

         备注:信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。


        1>. 格式: connect(信号发出者对象(指针), &className::clicked(), 信号接收者对象(指针), &classB::slot());
  2>. 标准信号槽的使用:
    connect(sender, &Send::signal, receiver, &Receiver::slot)


自定义信号和槽

        信号的声明是在头文件中进行的,QTsignals关键字指出进入了信号声明区,随后即可声明自己的信号。信号由moc自动生成,所以不需要在cpp中定义,只需要声明。

        槽是普通的 C++ 成员函数,可以被正常调用,它们唯一的特殊性就是很多信号可以与其相关联。当与其关联的信号被发射时,这个槽就会被调用。槽可以有参数,但槽的参数不能有缺省值。

槽函数分为三种类型,即 public slotsprivate slots protected slots

public slots:在这个区内声明的槽意味着任何对象都可将信号与之相连接。

protected slots:在这个区内声明的槽意味着当前类及其子类可以将信号与之相连接。

private slots:在这个区内声明的槽意味着只有类自己可以将信号与之相连接。

#include <QObject>
 // newspaper.h //
class Newspaper : public QObject
{
    Q_OBJECT   
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }
    void send()
    {
        emit newPaper(m_name);
    }
signals:
    void newPaper(const QString &name);
private:
    QString m_name;
};
 
// reader.h //
#include <QObject>
#include <QDebug>
 
class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}
    void receiveNewspaper(const QString & name)
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};
 
// main.cpp //
#include <QCoreApplication>
 
#include "newspaper.h"
#include "reader.h"
 
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
 
    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
                     &reader,    &Reader::receiveNewspaper);
    newspaper.send();
 
    return app.exec();
}

        首先看Newspaper这个类。这个类继承了QObject类。只有继承了QObject类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承QObject。凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。不管是不是使用信号槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。

● Newspaper类的 public 和 private 代码块都比较简单,只不过它新加了一个 signals。signals 块所列出的,就是该类的信号。信号就是一个个的函数名,返回值是 void(因为无法获得信号的返回值,所以也就无需返回任何值),参数是该类需要让外界知道的数据。信号作为函数名,不需要在 cpp 函数中添加任何实现。

●Newspaper类的send()函数比较简单,只有一个语句emit newPaper(m_name);。emit 是 Qt 对 C++ 的扩展,是一个关键字(其实也是一个宏)。emit 的含义是发出,也就是发出newPaper()信号。感兴趣的接收者会关注这个信号,可能还需要知道是哪份报纸发出的信号?所以,我们将实际的报纸名字m_name当做参数传给这个信号。当接收者连接这个信号时,就可以通过槽函数获得实际值。这样就完成了数据从发出者到接收者的一个转移。

  Reader类更简单。因为这个类需要接受信号,所以我们将其继承了QObject,并且添加了Q_OBJECT宏。后面则是默认构造函数和一个普通的成员函数。Qt 5 中,任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成实现代码。槽函数就是普通的成员函数,因此作为成员函数,也会受到 public、private 等访问控制符的影响。(如果信号是 private 的,这个信号就不能在类的外面连接,也就没有任何意义。)

自定义消息注意事项:

●发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);

●使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;

●槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;

●使用 emit 在恰当的位置发送信号;

●使用QObject::connect()函数连接信号和槽。

●任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数

信号和槽的使用 

         通过调用connect 函数来将某个对象的信号与另外一个对象的槽函数相关联,这样当发射者发射信号时,接收者的槽函数将被调用。

●一个信号可以和多个槽相连

        如果是这种情况,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪个后执行。

●多个信号可以连接到一个槽

  只要任意一个信号发出,这个槽就会被调用。

●一个信号可以连接到另外的一个信号

  当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

●槽可以被取消链接

        这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

●信号和槽的参数类型要对应

        信号的参数类型可以与槽的参数类型对应,信号的参数可以比槽的参数多,但不可以少,否则连接将失败。

 

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值