• 03Qt信号与槽(2)


    1. 元对象工具

    ​ 元对象编译器 MOC(meta object compiler)对 C++ 文件中的类声明进行分析并产生用于初始化元对象的 C++ 代码,元对象包含全部信号和槽的名字及指向这些函数的指针。

    ​ MOC 读 C++ 源文件,如果发现有 Q_OBJECT 宏声明的类,它就会生成另外一个 C++ 源文件,这个新生成的文件中包含有该类的元对象代码。例如,假设我们有一个头文件 mysignal.h,在这个文件中包含有信号或槽的声明,那么在编译之前 MOC 工具就会根据该文件自动生成一个名为 mysignal.moc.h 的 C++ 源文件并将其提交给编译器;类似地,对应于 mysignal.cpp 文件 moc 工具将自动生成一个名外 mysignal.moc.cpp 的文件提交给编译器。

    ​ 元对象代码是 signal/slot 机制所必须的。用 MOC 产生的 C++ 源文件必须和类实现一起进行编译和链接,或用 #inldue 语句将其包含到类的源文件中。MOC 并不扩展 #include 或 #define 宏定义,他只是简单的跳过所遇见的所有预处理指令。

    2. 程序示例

    ​ 这里给出了一个简单的示例程序,程序中定义了三个信号、三个槽函数,然后将信号和槽进行关联,每个槽函数值是简单的弹出一个对话框窗口。

    ​ 信号和槽函数的声明一般位于头文件中,同时在类声明单的开始位置必须加上 Q_OBJECT 语句,这条语句是不可或缺的,它将告诉编译器在编译之前必须先应用 MOC 工具进行扩展。关键字 signals 指出随后开始信号的声明,这里 signals 用的是复数形式而非单数,signals 没有 public、private、protected 等属性,这点不同于 slots。另外,signal、slots 关键字是 Qt 自己定义的,不是 C++ 中的关键字。

    ​ 信号的声明类似于函数的声明而非变量的声明,左边要有类型,右边要有括号,如果要向槽中传递参数的话,在括号中指定每个参数的类型,当然,形式参数的个数能多于一个。

    ​ 关键字 slots 指出随后开始槽的声明,这里 slots 用的也是复数形式。

    ​ 槽的声明和普通寒时候的声明相同,能携带零或多个形式参数。既然信号的声明类似于普通 C++ 函数的声明,那么,信号也可采用 C++ 中重载函数的形式进行声明,即同名但参数不同。例如,第一次定义的 void mySignal 没有带参数,而第二次定义的却带有参数,从这里我们能看到 Qt 的信号机制是非常灵活的。

    ​ 信号和槽之间的联系必须事先用 connect 函数进行指定。如果要断开二者之间的联系,使用函数 disconnect。

    //mainwindow.h
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
    
    signals:
        //信号声明区
        void mySignal();
        void mySignal(int x);
        void mySignalPraam(int x, int y);
    public slots:
    	//槽声明区
        void mySlot();
        void mySlot(int x);
        void mySlotParam(int x, int y);
    private slots:
        void on_pushButton_clicked();
    };
    
    #endif // MAINWINDOW_H
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QMessageBox>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        connect(this, SIGNAL(mySignal()), SLOT(mySlot()));
        connect(this, SIGNAL(mySignal(int)), SLOT(mySlot(int)));
        connect(this, SIGNAL(mySignalPraam(int,int)), SLOT(mySlotParam(int,int)));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::mySlot()
    {
        QMessageBox::about(this, "MainWindow","This is a signal/slot sample without parameter!");
    }
    void MainWindow::mySlot(int x)
    {
        QMessageBox::about(this, "MainWindow", "This is a signal/slot sample with one parameter!");
    }
    void MainWindow::mySlotParam(int x, int y)
    {
        char s[256];
        sprintf(s, "x:%d  y:%d", x, y);
        QMessageBox::about(this, "MainWindow", s);
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        //发射信号
        emit mySignal();
        emit mySignal(500);
        emit mySignalPraam(100, 200);
    }
    
    3. 应注意的问题

    ​ 信号和槽机制是比较灵活的,但有些局限性我们必须了解,这样在实际的使用过程中才能做到有的放矢,避免产生一些错误。

    (1)信号和槽的效率是非常高的,不过同真正的回调函数比起来,由于增加了灵活性,因此在速度上海市有所损失,当然这种损失相对来说是比较小的,通过在一台 i586-133 的集市上测试是 10 微秒(运行Linux),可见这种机制所提供的简洁性、灵活性还是值得的。但如果我们要追求高效率的话,比如在实时系统中就要尽可能的少用这种机制。

    (2)信号和槽机制和普通函数的调用相同,如果使用不当的话,在程序执行时也有可能产生死循环。因此,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射所接受到的同样信号。例如,在前面给出的例子中如果在 mySlot 槽函数中加上语句 emit Sginal 即可形成死循环。

    (3)如果一个信号和多个槽相联系的话,那么,当这个信号被发射时,与之相关的槽被激活的顺序是随机的。

    (4)宏定义不能用在 signal 和 slot 的参数中。既然 MOC 工具不扩展 #define,因此, 在 signals 和 slots 中携带参数的宏就不能正确地工作,如果不带参数则是可以的。

    (5)构造函数不能用在 signals 或 slots 声明区域内。

    (6)函数指针不能作为信号或槽的参数。

    class someClass:public QObject
    {
    	Q_OBJECT
    	...
    public slots:
    	void apply(void(*applyFunction)(QList*, void*), char*);	
    }
    //但是,可以通过采用函数指针的 typedef 来绕过这个限制
    typedef void(*ApplyFunctionType)(QList*, void*);
    class someClass:public QObject
    {
    	Q_OBJECT
    	...
    public slots:
    	void apply(AppfunctionType, char*);	
    }
    

    (7)信号和槽不能有缺省值。

    ​ 既然 signal/slot 绑定时发生在运行时的,那么,从概念上讲使用缺省参数是困难的。

    (8)信号和槽也不能携带模板类参数。

    ​ 如果将信号、槽声明为模板类参数的话,即时 MOC 工具不报告错误,也不可能得到预期的结果。

    (9)嵌套的类不能位于信号或槽区域内,也不能有信号或槽。

    (10)友元声明不能位于信号或才声明区内。相反,它们应该在普通 C++ 的 private、protected或public 区内进行声明。

    道虽迩,不行不至;事虽小,不为不成。
  • 相关阅读:
    mysql 在orderby和limit混合使用时重复数据问题
    springboot启动类 注解
    redis RDB和AOF两种持久化的区别
    C#解析逻辑字符串【x>y&&a>b||p=r】
    删除例如联想笔记本系统隐藏分区
    通过贝叶斯算法实现自动识别类别
    将可执行exe文件注册成windows服务
    Windows10中的IIS10安装php manager和IIS URL Rewrite 2.0组件的方法
    添加钩子监听全局鼠标或键盘事件
    C# DateTime.Now和DateTime.UtcNow的区别
  • 原文地址:https://www.cnblogs.com/rock-cc/p/9360252.html
Copyright © 2020-2023  润新知