> 文档中心 > Qt学习笔记(2)

Qt学习笔记(2)

目录

一.信号与槽

二.Qt内存回收机制


一.信号与槽

看了一些资料,最后总结就是:信号和槽的表现形式都是函数,信号和槽之间的关系就像你跟你妈一样,妈妈叫你去吃饭,她这个时候就发出了 “叫你去吃饭的信号” ,怎么处理这个信号完全由你自己决定,你可以忽视它不作回应,如果你想要回应,那就需要一个connect函数连接你的回应跟你妈妈的信号,而你的回应就是槽。你的回应可以有多种,你可以说 “我不吃” ,也可以说 “我来吃饭了” ,这说明,一个信号可以连接不同的槽;你这些话也可以面对不同信号时回答,所以一个槽也可以被几个信号连接。

那我们要如何使用呢?下面来编写一个案例。

这里我们是自定义信号槽使用,所以需要知道注意事项:

如果想要使用自定义的信号和槽, 首先要编写新的类并且让其继承Qt的某些标准类,我们自己编写的类想要在Qt中使用使用信号槽机制, 那么必须要满足的如下条件:

  • 这个类必须从QObject类或者是其子类进行派生
  • 在定义类的第一行头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:class MyMainWindow : public QWidget{    Q_OBJECTpublic:    ......}

自定义信号:

  1. 信号是类的成员函数
  2. 信号的返回值是void类型,且能够重载
  3. 信号需要用signals关键字进行声明,且只需要声明,不需要实现
  4. 在发送信号时,可以用空宏emit标识一下,表示这是一个信号
//在类中signals://信号,不需要实现,函数声明就好    void testSignal();    void hungry();    void hungry(const QString);//支持重载

自定义槽:

  1. 函数返回值是void类型
  2. 槽函数写起来相比信号函数稍微多一点约束,槽函数最好不要做缺省
  3. 槽函数的参数个数小于或等于跟他连接的信号参数个数,槽函数也支持重载
  4. 槽函数可以是普通成员函数,也可以是静态成员函数,也可以是全局函数,也可以写成匿名的Lambda表达式
  5. 槽函数前面用slots关键字声明,虽然可以省略,但是标识作用能写就写
//在类中,自定义槽函数public slots:    void print();

信号与槽的连接:(重点)

  1. 使用函数指针的连接方法,形式为 connect(发送者指针,信号函数指针,接受者指针,槽函数指针)
  2. 使用QOverload和of联动的方式,形式为 connect(发送者指针,QOverload::of(&函数地址),接受者指针,QOverload::of(&函数地址))
  3. 使用Qt4中的方法,使用SIGNAL和SLOT宏,形式为 connect(发送者指针,SIGNAL(信号函数名(参数1,参数2,...)),接受者指针,SLOT(槽函数名(参数1,参数2,...)))

方法三不推荐使用,原因如下:

这种方式在进行信号槽连接的时候, 信号槽函数通过宏SIGNALSLOT转换为字符串类型。因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错,但实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位

现在给出一个完整的案例供参考:

widget.h

class Widget : public QWidget{    Q_OBJECT  //要在qt中使用信号与槽,必须使用public:    Widget(QWidget *parent = nullptr);    ~Widget();public slots:    void onBtnClicked();    void onHungry();    void onHungry(const QString str);private:    myButton pika;};

button.h

class myButton : public QPushButton{    Q_OBJECTpublic:    explicit myButton();signals://信号,不需要实现,函数声明就好    void hungry();    void hungry(const QString);};

widget.cpp

Widget::Widget(QWidget *parent)    : QWidget(parent){    //先来一个按钮,要传指针    QPushButton* btn = new QPushButton;    btn->setText("关闭");    //显示按钮    btn->show();    //链接信号与槽    connect(btn,&QPushButton::clicked,this,&Widget::close);    //如果信号有重载,会产生二义性问题    connect(&pika,&myButton::hungry,this,&Widget::onHungry);    //1.1类的成员函数指针    void(myButton::*h)() = &myButton::hungry;//定义   了指向无参信号的指针    connect(&pika,h,this,&Widget::onHungry);    void(myButton::*hh)(const QString) = &myButton::hungry;    connect(&pika,hh,this,&Widget::onHungry);    //2.QOverload加of    connect(&pika,QOverload::of(&myButton::hungry),     this,QOverload::of(&Widget::onHungry));    connect(&pika,QOverload::of(&myButton::hungry),     this,QOverload::of(&Widget::onHungry));    //3.使用QT4的方法,不推荐,这两个宏实际上是把信号或槽函数转成了字符串,不容易看出错误    connect(&pika,SIGNAL(hungry()),this,SLOT(onHungry()));    connect(&pika,SIGNAL(hungry(const QString)),this,SLOT(onHungry(const QString)));    //lambda表达式做槽函数    QPushButton *btn1 = new QPushButton(this);    btn1->setText("我是按钮");    btn1->move(200,0);    //可以使用=或者this捕获,但是最好不要用引用捕获    connect(btn1,&QPushButton::pressed,this,[this](){ this->close();    });}Widget::~Widget(){}void Widget::onBtnClicked(){    //触发信号   qDebug()<<"已经发送";   emit pika.hungry();   emit pika.hungry("汉堡");}void Widget::onHungry(){    qDebug()<<"已经收到,做出反馈";}void Widget::onHungry(const QString str){    qDebug()<<"okok";}

二.Qt内存回收机制

我们知道有子类和父类,在Qt中,有子对象和父对象,这个关系是组件跟组件之间的,为什么要这么做呢?

比如我们现在创建了一个窗口,在窗口内new了一个按钮,但是很尴尬的是,你不知道在哪写delete,如果在下面写,按钮则会一闪而过,如果不写,按钮则会导致内存泄漏,这个时候可能有人会说,那把对象分配到栈上不就行了,确实可以,不过当元素越来越多,栈的空间不就被占用得很严重吗?所以Qt提出了一种新的方法:子控件可以指定父对象,让父对象自动管理内存

在Qt中,最基础和核心的类是:QObject,QObject内部有一个list,会保存children,还有一个指针保存parent,当自己析构时,会自己从parent列表中删除并且析构所有的children。

 

    QPushButton *btn = new QPushButton(this);    connect(btn,&QPushButton::clicked,this,&Widget::onBtnClicked);    btn->setText("按钮");

这个是上面写的代码,内存泄漏了,可以这么优化。