1.Qt面试题
-
Qt三大核心机制
???
-
什么是Qt的元对象系统?
Qt的元对象系统是Qt框架中一个核心的组成部分,
提供了一种机制来拓展C++的功能,
Qt的元对象系统(Meta-ObiectSystem)
提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
元对象系统由以下三个基础组成。
1. QObiect 类是所有使用元对象系统的类的基类。
2. 在一个类的private部分声明QOBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
3. MOC(元对象编译器)为每个QObject的子类提供必要的代码来实现元对象系统的特性。
-
元对象编译器
Qt的元对象编译器(Meta-Object Compiler,MOC)
是一个预处理器,在源程序被编译前,
先将这些Qt 特性的程序转换为标准 C++兼容的形式,然后再由标准 C++编译器进行编译。
这就是为什么在使用信号与槽机制的类里,必须添加一个Q OBJECT 宏的原因,
只有添加了这个宏,moc 才能对类里的信号与槽的代码进行预处理。
-
使用信号和槽机制为什么需要添加Q_OBJECT宏?
因为Q_OBJECT宏启用了元对象系统的功能,
而元对象系统是Qt框架中实现信号和槽以及其他高级特性的基础。
当类中添加了Q_OBJECT宏,也就是告诉了元对象编译器这个类需要特殊处理,
元对象编译器(moc)才会生成信号和槽的注册信息,
有了这些注册信息,Qt才能在运行的时候识别和连接信号槽。
-
对象树与拥有权
Qt 中使用对象树(object tree)来组织和管理所有的 QObject 类及其子类的对象,
当创建一个 QObiect 对象时,如果使用了其他的对象作为其父对象(parent),
那么这个QObject 对象就会被添加到父对象的 children()列表中;
当父对象被销毁时,这个 QObject也会被销毁。
当关闭窗口后,因为该窗口是顶层窗口,关闭后没有可以显示的窗口,
所以应用程序要销毁该窗口部件(如果不是顶层窗口,那么关闭时只是隐藏,不会被销毁),
而当窗口部件销毁时会自动销毁其子部件。
还有一种重定义父部件(reparented)的情况
,例如,将一个包含其他部件的布局管理器应用到窗口上,
那么该布局管理器和其中所有部件都会自动将它们的父部件转换为该窗口部件。
可以使用children()函数来获取一个部件的所有子部件的列表。
-
简述信号和槽机制,优点是什么?
比较灵活:
一个信号可与多个槽函数相连接,一个槽函数可以响应多个信号
类型安全:
即信号的参数类型和参数个数同接受该信号的槽的参数类型和个数相同,
就算不能达到类型相同,也要能进行转化,比如int--->double
支持多线程
信号与槽支持跨线程通讯,允许开发者在多线程的环境中安全的进行对象间通信
松散耦合,
信号的发出者无需知道是哪个对象接收信号,
信号的接收者也无需知道是哪个对象发送的信号,
降低了对象间的耦合度,提高了代码的模块化
速度较慢:
1.需要定位信号的接收者
2.需要安全的遍历所有关联槽
3.编组、解组传递参数
4.在多线程中信号需要排队等待
-
信号和槽是否支持重载
支持重载,只要信号和槽的参数类型不同,就可以重载
(感觉应该和函数重载条件差不多)
-
connect()函数
connect()函数,里面有5个参数,
(发出信号的对象,信号函数地址,接收信号的对象,槽函数地址,第五个参数)
QueuedConnection(队列连接)
槽函数运行于信号接收者所在的线程
当信号发送后, 槽函数不会被立即调用,
当控制返回信号接收者所在线程的事件循环后,槽函数才会被调用。
同时无论槽函数是否被执行,信号后面的代码都会立即执行。
多线程下使用这个类型
DirectConnection(直接连接)
槽函数运行于信号发送者所在的线程
信号发送后,槽函数立即被调用,直到槽函数执行完毕后,信号后面的代码才执行
多线程使用时容易造成崩溃
AutoConnection(自动连接)
默认值,如果使用这个,连接类型会在信号发射时决定。
如果信号接收者与发送者在同一线程,则自动使用DirectConnection
如果不在同一线程,则自动使用QueuedConnection
BlackingQueuedConnection(阻塞连接)
槽函数的调用时机与队列连接一样,但是信号发送者所在的线程会被阻塞,直到槽函数执行完毕。
这种连接方法要求发送者和接收者不能在同一线程,否则会造成死锁。这个参数在多线程需要同步时可以使用。
该连接的一个使用场景是,
当需要通过一个GUI程序来启动另一个窗口时,
可以先判断当前的线程是否为主线程(即 GUI线程),
如果当前线程为GUI线程中,则可以直接像调用普通函数一样来调用对应的槽函数,
否则需要通过信号槽的方式来触发对应的信号,等待槽函数在另一个线程中来执行。
UniqueConnection(唯一连接)
避免了信号和槽的重复连接。可以通过按位或(|)与其他四个参数一起使用。
-
事件的定义
Qt 中使用一个对象来表示一个事件,继承自 QEvent 类。
事件是用于描述程序内部或外部发生的动作,
任意的 QObject 类实例及其子类实例都可以接收和处理事件。
-
常见事件
键盘事件(QKeyEvent),鼠标事件(QMouseEvent)
定时器事件(QTimerEvent),绘图事件(QPaintEvent),
自定义事件(QEvent)
拖拽事件,
-
事件的传递
当事件产生之后,Qt使用 应用程序对象 调用notify()函数将事件发送到指定的窗口:
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
事件在发送过程中可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤。
// 需要先给窗口安装过滤器, 该事件才会触发
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类:
[override virtual protected] bool QWidget::event(QEvent *event);
事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)
分发给对应的事件处理器函数进行处理,
每个事件处理器函数都有默认的处理动作(我们也可以重写这些事件处理器函数),
比如:鼠标事件:
// 鼠标按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
如果焦点部件对事件进行了忽略,那么事件将交由焦点部件的父部件来处理
在实现自定义的事件处理函数时,一般也要调用父类的事件处理函数来实现默认的操作
-
事件与信号区别
信号与槽通常一起用于对象之间的信息通讯。
事件是用于一个对象内部的一些功能的实现
在“使用”窗口部件时,我们经常需要使用信号,并且会遵循信号与槽的机制;
在“实现”窗口部件时,我们就不得不考虑如何处理事件了。
-
事件机制的五种级别的事件过滤
1.重新实现特定事件的处理函数
比如:KeyPressEvent(),MousePressEvent(),PaintEvent()
2.重新实现事件分发器QObject::Event()函数
重新实现Event()函数可以在事件到达特定的事件处理函数之前对其进行处理。
同时也可以用来处理一些没有特定事件处理函数的事件。
在重新实现Event()函数时,需调用基类Event()函数用来处理我们没有处理的事件函数。
3.在对象上安装事件过滤器
比如使用A监视B的事件,
使用B调用 (印仕套 event 飞由特) installEventFilter()函数,
并以A的地址作为参数,这样发送到B的事件都由A的EventFilter()函数处理,
之后重写EventFilter()函数,在里面实现对事件的处理。
若一个对象有多个事件过滤器,过滤器按照顺序激活,先到达最后安的装监视对象,最后到达先安装的监视对象。
4.给QApplication对象安装事件过滤器
给QApplication类的qApp对象安装事件过滤器,那么所有对象的事件都要最先经过EventFilter()函数。
5.继承QApplication类,并重写notify()函数
该方法使用notify()函数发送事件,
使用该方法是事件到达事件过滤器之前得到所有事件的唯一方法。
但是通常来说,使用事件过滤器会更好些,因为不需要继承QApplication,
并且可以安装多个事件过滤器
-
Qt多线程的创建方式
第一种:
创建一个继承了QThread类的子类,命名为MyThread
在MyThread中重写父类的run()方法
在主线程中创建MyThread类对象child_Thread
然后使用该child_Thread对象调用start()函数启动子线程
优缺点
优点:创建和使用时简单
缺点:当在一个子线程中处理多个任务时,
所有任务的代码都将写在run函数中,
这样run函数中的处理逻辑会混乱,不易维护
第二种
创建一个名为MyThread的类,使得该类从QObject中派生
在MyThread中创建一个函数,将其命名为working(命名可以任意)
在working函数中编写子线程执行的业务逻辑
在主线程中创建QThread类对象child_Thread,这个对象就是子线程的对象
在主线程中创建MyThread类对象work
将work移动到创建的子线程对象child_Thread中,使用work调用函数moveToThread
在主线程中使用child_Thread调用start启动线程
但是此时子线程虽然启动,但是移动到线程中的对象并没有工作
所以需要调用MyThread类中的working函数
优点:用起来更加灵活,更加便以维护
-
Qt的线程同步
无
感觉还是先背会C++的线程同步吧
-
Qt 信号和槽的本质是什么
回调函数。信号或是传递值,或是传递动作变化;槽函数响应信号或是接收值,
或者根据动作变化来做出对应操作。
-
自定义控件流程
来自GPT,不一定对
创建自定义控件类:并继承其他控件。
实现必要的方法:重写 paintEvent() 和事件处理方法(如 mousePressEvent())。
添加属性和信号:使用 Q_PROPERTY (怕破忒)声明属性,并通过 signals (赛格no死)定义信号。
在应用程序中使用:在主窗口或其他控件中实例化和使用自定义控件。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示