Qt学习笔记(2)
目录
一.信号与槽
二.Qt内存回收机制
一.信号与槽
看了一些资料,最后总结就是:信号和槽的表现形式都是函数,信号和槽之间的关系就像你跟你妈一样,妈妈叫你去吃饭,她这个时候就发出了 “叫你去吃饭的信号” ,怎么处理这个信号完全由你自己决定,你可以忽视它不作回应,如果你想要回应,那就需要一个connect函数连接你的回应跟你妈妈的信号,而你的回应就是槽。你的回应可以有多种,你可以说 “我不吃” ,也可以说 “我来吃饭了” ,这说明,一个信号可以连接不同的槽;你这些话也可以面对不同信号时回答,所以一个槽也可以被几个信号连接。
那我们要如何使用呢?下面来编写一个案例。
这里我们是自定义信号槽使用,所以需要知道注意事项:
如果想要使用自定义的信号和槽, 首先要编写新的类并且让其继承Qt的某些标准类,我们自己编写的类想要在Qt中使用使用信号槽机制, 那么必须要满足的如下条件:
- 这个类必须从QObject类或者是其子类进行派生
- 在定义类的第一行头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:class MyMainWindow : public QWidget{ Q_OBJECTpublic: ......}
自定义信号:
- 信号是类的成员函数
- 信号的返回值是void类型,且能够重载
- 信号需要用signals关键字进行声明,且只需要声明,不需要实现
- 在发送信号时,可以用空宏emit标识一下,表示这是一个信号
//在类中signals://信号,不需要实现,函数声明就好 void testSignal(); void hungry(); void hungry(const QString);//支持重载
自定义槽:
- 函数返回值是void类型
- 槽函数写起来相比信号函数稍微多一点约束,槽函数最好不要做缺省
- 槽函数的参数个数小于或等于跟他连接的信号参数个数,槽函数也支持重载
- 槽函数可以是普通成员函数,也可以是静态成员函数,也可以是全局函数,也可以写成匿名的Lambda表达式
- 槽函数前面用slots关键字声明,虽然可以省略,但是标识作用能写就写
//在类中,自定义槽函数public slots: void print();
信号与槽的连接:(重点)
- 使用函数指针的连接方法,形式为 connect(发送者指针,信号函数指针,接受者指针,槽函数指针)
- 使用QOverload和of联动的方式,形式为 connect(发送者指针,QOverload::of(&函数地址),接受者指针,QOverload::of(&函数地址))
- 使用Qt4中的方法,使用SIGNAL和SLOT宏,形式为 connect(发送者指针,SIGNAL(信号函数名(参数1,参数2,...)),接受者指针,SLOT(槽函数名(参数1,参数2,...)))
方法三不推荐使用,原因如下:
这种方式在进行信号槽连接的时候, 信号槽函数通过宏SIGNAL
和SLOT
转换为字符串类型。因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错,但实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位。
现在给出一个完整的案例供参考:
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("按钮");
这个是上面写的代码,内存泄漏了,可以这么优化。