信号槽机制#
信号槽是一种类型安全的对象间通信的机制。当一个对象状态发生变化时,发出一个信号 signal通知事件,其他对象可以连接这个信号到一个 slot 槽函数上响应这些事件,从而实现松耦合的通信。信号槽机制替代传统的、不安全的回调函数(如函数指针),槽函数可以是虚函数。
实现原理:单线程主要是由观察者模式与函数指针的联合使用,多线程本质上是事件机制
连接方法:UI信号槽自动连接、Lamda表达式、自定义信号槽
连接方式:
- 自动连接(Auto Connection) :默认方式,Qt 会自动决定信号与槽是同步还是异步调用,取决于信号和槽是否在同一线程。
- 直接连接(Direct Connection) :信号发出时直接在发出者所在的线程中调用槽。
- 队列连接(Queued Connection) :将信号的调用排入目标对象线程的事件队列,槽会在目标线程中执行。
- 阻塞队列连接(BlockingQueuedConnection) :(信号和槽必须在不同的线程中,否则就产生死锁):这个是完全同步队列,只有槽线程执行完成才会返回,否则发送线程也会一直等待,相当于是不同的线程可以同步起来执行
- 独立连接(Unique Connection) :确保信号和槽之间的连接是唯一的,避免重复连接。
优点:
- 松耦合 :信号与槽之间不直接依赖,便于扩展和修改。
- 线程安全 :在多线程环境下,Qt 会自动处理信号与槽的同步。
- 易于维护 :信号与槽的方式使得代码结构清晰,维护性好。
缺点:
- 性能开销 :信号和槽的调用机制会增加一定的性能开销。
- 调试困难 :如果连接不当,可能会导致难以追踪的问题。
一个信号可以连接多个槽,信号发射时,所有连接的槽会依次被调用。一个槽可以响应多个信号,只要它的参数签名与信号匹配。
自定义信号和槽:
- 自定义信号:在类的
signals部分使用signals关键字定义 - 自定义槽:在类的
public slots或private slots部分定义
元对象系统(Meta-Object System)#
元对象系统是 Qt 提供的一种机制,用于支持信号与槽、动态属性、类型识别等功能。
它依赖于 Q_OBJECT 宏,并通过元对象编译器(MOC)生成元对象信息,使得 Qt 可以在运行时获取对象的类型信息。
- 组成:
QObject类,MOC,信号与槽机制 - 作用:提供信号槽机制实现对象之间的高效通信,动态属性、类型识别的功能
Q_OBJECT 宏#
Q_OBJECT 宏启用 Qt 的元对象系统,支持信号与槽机制、动态属性、对象反射等功能。每个使用信号与槽的类都必须声明该宏,以便 MOC 生成相关的元对象代码。
元对象编译器(MOC)#
MOC 是 Qt 提供的工具,负责处理含有 Q_OBJECT 宏的类。它会生成包含元对象信息的代码,以支持信号与槽、动态属性等功能。生成的代码会被编译并链接到最终的应用程序中。
内存管理#
Qt 使用 父子对象机制 来自动管理内存。每个对象如果有父对象,父对象会在销毁时自动销毁所有子对象。没有父对象的对象需要手动调用 delete 来销毁
对象树机制#
Qt 的 对象树机制 是一种父子对象关系管理方式。每个 QObject 对象都有一个父对象,父对象会自动销毁其所有子对象。父子关系在内存管理中非常重要,可以简化资源管理,避免手动 delete 。
- 父对象销毁了子对象一起被销毁,避免内存泄漏
- 智能指针
QSharedPointerQWeakPointer,处理对象之间引用的复杂情况 - 内存分配策略及优化
deleteLater() 延迟销毁对象#
deleteLater() 函数将对象的销毁操作推迟到当前事件处理完毕后执行,避免在事件处理过程中删除对象引起的崩溃或未定义行为。常用于异步删除对象。
事件处理机制#
Qt 的事件机制基于事件循环和事件队列,所有事件都会先进入队列,然后依次分发到合适的控件。
事件传递的顺序:
- 事件首先由事件源对象接收并放入事件队列
- 事件队列中的事件会依次传递给目标对象,首先传递到事件接收对象,每个继承
QObject的都可以通过重写事件处理函数来处理特定的事件 - 如果该对象处理了事件,则停止传递,否则,事件会传递到该对象的父对象,直到根对象
- 如果事件一直没有被处理,Qt会使用默认的事件处理机制
自定义事件处理:继承 QEvent 类
event() 和 eventFilter()#
event()是每个QObject的成员函数,主要用于处理传递给对象的事件。可以通过重写此函数来处理不同类型的事件(如鼠标、键盘等)eventFilter()用于设置事件过滤器,拦截传递到目标控件的事件,在事件到达目标控件前对其进行处理。它是通过安装到目标对象上的方式来拦截事件的
事件过滤器的实现#
- 创建一个类继承
QObject并重写eventFilter() - 使用
installEventFilter()方法将事件过滤器安装到目标控件上,这样目标控件的事件就会先经过过滤器
GUI常用事件函数#
paintEvent():用于绘制控件的外观,当控件需要重新绘制时(如大小变化或需要更新显示)会调用此函数。resizeEvent():当控件的大小发生变化时调用,通常用来调整控件的布局或重新绘制内容。mousePressEvent():当鼠标按下时调用,用于处理鼠标点击事件,如点击按钮或拖动控件等。
事件循环#
Qt 的事件循环机制是指程序启动后,通过一个循环不断从事件队列中取出事件并分发处理的过程。
事件循环由 QEventLoop 实现,应用程序启动后进入事件循环,只有在事件循环运行时,Qt 的界面刷新、信号槽、定时器等机制才能正常工作。
exec() 启动事件循环#
QApplication::exec():启动整个应用程序的主事件循环QDialog::exec():启动一个局部事件循环,使对话框以模态方式运行
当调用 exec() 后,程序会阻塞在此函数中,直到事件循环结束,函数才会返回
QCoreApplication::processEvents()#
手动处理当前事件队列中的事件,常用于在耗时操作中避免界面卡死 / 强制刷新界面 / 临时处理待处理的事件,它会立即处理当前队列中的事件,但不会进入完整的事件循环
缺点:
- 频繁调用可能导致逻辑混乱
- 不能替代事件循环
- 更推荐使用多线程或异步方式解决耗时问题
定时器#
定时器的实现方式#
QTimer最常用的定时器方式,基于事件循环,支持单次和周期定时QObject::startTimer()/timerEvent()通过对象启动一个底层定时器,在timerEvent()中处理超时事件,适合轻量级定时需求QBasicTimer是对startTimer()的封装,效率较高,常用于对性能要求较高的场景QElapsedTimer用于测量时间间隔,不触发超时事件,主要用于性能统计和耗时测量- 系统定时器(结合线程)在子线程中配合
QTimer或系统接口实现更高精度的定时控制
QTimer#
QTimer 是 Qt 中最常用的定时器类,使用方式主要包括以下几步:
- 创建
QTimer对象 - 连接
timeout()信号到槽函数 - 调用
start()启动定时器,设置时间间隔(毫秒)
QTimer 定时器启动后,每到达设定的时间间隔就会触发 timeout 信号,在槽函数中处理定时任务
QTimer timer; // 定时器定时刷新
QObject::connect(&timer, &QTimer::timeout, timer_task);
timer.start(5000); // 5s 执行一次 事件驱动cpp可以使用 setSingleShot(true) 设置为单次定时器,也可以使用静态函数 QTimer::singleShot() 实现一次性延时执行
startTimer()#
startTimer() 是 QObject 提供的底层接口,而 QTimer 是对定时器机制的高级封装。
主要区别有:
- 使用方式不同:
startTimer()需要重写timerEvent()来处理定时事件,QTimer使用信号与槽机制 - 功能丰富程度不同:
QTimer支持单次定时、暂停、重启等功能,startTimer()功能相对简单 - 可读性和维护性:
QTimer更符合 Qt 编程风格,startTimer()更偏底层,适合对性能敏感的场景
一般情况下推荐使用 QTimer ,除非有特殊性能或底层控制需求。
隐式共享#
对于两份内容相同的数据,不用重新开辟一份内存来存储,而是使用一份浅拷贝即只拷贝一个指针,这样两份数据就共用一份内存和数据,浅拷贝一次引用计数就增加,计数变成0的时候就删除共享的数据。