文章目录
- 一 、对象树是什么?
- 二、信号和槽的基本概念
- 2.1 信号
- 2.2 槽
- 2.3 松散耦合
- 2.4 特点
- 三、示例
- 总结
一 、对象树是什么?
对象树是由父类和若干子类对象组成,而子类也可以由若干孙类。
QT中的对象树是以QObject为起始父类来完成树的构建的,如下图所示。
当创建子类对象(QObject )的时候,会传递一个parent指针给子类的构造函数,该指针就是父对象指针。
//QWidget 为myWidget的父类
myWidget::myWidget(QWidget *parent): QWidget(parent)
{
}class myWidget : public QWidget
{Q_OBJECT
//parent 父对象指针传递给子类的构造函数。
public:myWidget(QWidget *parent = 0);
};
所以在创建 QObject 对象时,会提供一个父对象的指针给构造函数,QObject 对象就会自动添加到其父对象的 children() 列表。然后当父对象进行析构的时候,这个子对象列表中的所有对象都会被析构,当析构子对象的时候,会自动从父对象的子对象列表中删除。
这种对象树机制的优点在于析构父类的析构时,子类对象会自动销毁,这样就不需要关注控件内存的销毁情况。
但也会存在特殊情况会造成QT程序内存泄漏问题,这样就需要我们手动删除或者调用析构函数。
1:parent指针不会区分child对象是new出来的还是从栈上分配的,如果delete栈上的内存,就会发生错误。避免这一情况的方法就是全部使用new来开辟空间。
2:可以尝试采用智能指针QPointer(模板类)的方法来自动销毁内存空间。
3:Qt的对象清理器QObjectCleanupHandler也可以自动删除所有监视对象。
二、信号和槽的基本概念
信号槽机制类似于C#中回调函数的概念,我们知道,回调函数的本质是函数作为参数在合适的时候(条件成立时)被某一特定的事件调用的方法,而信号槽机制是如果触发事件,对象就会发出信号给接收者,然后调用绑定的槽函数,其中相似之处在于这两种方式都会在满足某一条件时触发事件,信号类似于条件达成的方法,槽函数就像被委托调用的方法。
C#是通过委托来实现函数作为参数传递的,信号槽机制则是通过连接的方式实现的信号和槽的绑定。
2.1 信号
信号signal也是由不同的方法组成的,例如,按钮的信号有9种,分别继承自三个类QAbstractButton,QWidget,QObject:
分别表示点击、按压、松开、开关等不同状态。
我们可以在自定义类里自定义信号:
signals://自定义信号写到signals下//返回值是void,只需要声明,不需要实现//可以有参数,可以重载//emit hungry();来发送信号void hungry();void hungry(QString food);
2.2 槽
槽(slot)的本质是类的成员函数,其参数可以是任意类型的,可以实现不同的功能,如关闭,隐藏,下降,上升等。例如QWidget类下槽函数包括:
自定义槽:
public slots://返回值是void,需要声明,也需要实现//可以有参数,也可以重载void eat();void eat(QString food);
2.3 松散耦合
信号和槽是松散耦合的。
松散耦合是指槽可以与信号连接(connect)在一起,只有连接的信号被激发的时候,才能调用槽函数。
2.4 特点
1:一个信号可以连接多个槽函数。多个信号也可以连接同一个槽函数。
2:信号和槽函数,参数必须一一对应。
三、示例
使用一个定时器的demo做一个简单的示例。
定义两个定时器,实现显示数字的功能。
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);//启动定时器,每隔1s启动一次id1 =startTimer(1000);id2 =startTimer(2000);//定时器的一种方式QTimer *timer = new QTimer(this);timer->start(500);//超时信号和显示槽函数建立连接,timer一超时便发送一个信号setText方法递增1connect(timer,&QTimer::timeout,[=](){static int num = 1;ui->label_3->setText(QString::number(num++));});//点击暂停按钮实现connect(ui->pushButton,&QPushButton::clicked,[=](){timer->stop();});
}
另外一种方法需要定义定时器的事件,用来显示数字。
void Widget::timerEvent(QTimerEvent *event)
{//number()方法转stringif(event->timerId() == id1){static int num = 1;ui->label->setText(QString::number(num++));}if(event->timerId() == id2){static int num2 = 1;ui->label_2->setText(QString::number(num2++));}
}
总结
对象树和信号槽机制使QT界面功能的搭建变得简易和灵活,缺点在于由于需要遍历所有关联造成性能降低。