零、基本概念
什么是信号槽?
信号槽类似于软件设计模式中的观察者模式,(观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。)被观察者发出的信号(signal),观察者收到自己注册监听signal,就通过槽(slot)关联的槽函数function实现动作操作。
信号槽的优缺点
(1)类型安全
信号的参数类型、参数个数需要和槽函数的参数类型和参数个数一致。槽函数的个数可以少于信号的参数个数,但缺少的参数必须是信号参数的最后一个或最后几个。
(2)松散耦合
信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于QObject。
(3)效率
信号槽增强了对象间通信的灵活性,同时损失了一些性能,通过信号调用的槽函数比直接调用速度慢约10倍(因为需要定位信号接收者;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调用速度对性能要求不是非常高的场景是可以忽略的,是满足绝大部分场景。
信号槽的用法和性质
connect(sender, signal, receiver, slot);
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
系统自带的信号:
void clicked(bool checked = false)
void pressed()
void released()
void toggled(bool checked )signals inherited from Qwidgetsignals inherited from Q0bject
- 一个信号可以关联多个槽函数。
- 多个信号可以关联一个槽函数
- 一个信号可以关联另一个信号
- 一个信号关联多个信号
一、Qt核心机制
- 信号与槽(Signals & Slots)
- 问:Qt的信号槽机制与回调函数有何区别?
- 答:
- 解耦性:信号槽通过元对象系统(Meta-Object System)连接,发送者无需知道接收者。
- 参数类型安全:编译时检查参数类型(需用
Q_DECLARE_METATYPE
注册自定义类型)。 - 多对多通信:一个信号可连接多个槽,反之亦然。
- 线程安全:支持跨线程通信(自动选择
Qt::AutoConnection
模式)。
- 元对象系统(Meta-Object System)
- 问:
moc
(元对象编译器)的作用是什么? - 答:
- 处理
Q_OBJECT
宏,生成moc_*.cpp
文件,实现信号槽、动态属性、运行时类型信息(RTTI)等功能。 - 支持
qobject_cast
进行安全的类型转换。
- 处理
- 问:
二、内存管理与对象模型
- 父子对象机制
- 问:Qt如何管理对象的生命周期?
- 答:
- 通过
QObject
的父子关系:父对象析构时自动销毁所有子对象。 - 示例:
QWidget* child = new QWidget(parent);
- 通过
- 智能指针
- 问:如何在Qt中使用智能指针?
- 答:
- 使用
QSharedPointer
、QWeakPointer
管理动态对象。 - 注意:
QObject
及其子类通常依赖父子关系管理,需谨慎混合使用。
- 使用
三、多线程与并发
- QThread的两种用法
- 问:继承
QThread
与使用moveToThread
有何区别? - 答:
- 子类化QThread:重写
run()
方法,适用于需要控制线程执行流程的场景。 - moveToThread:将对象移动到新线程,通过信号槽触发逻辑(更符合事件驱动模型)。
- 子类化QThread:重写
- 问:继承
- 线程同步
- 问:Qt中如何实现线程同步?
- 答:
- 使用
QMutex
、QReadWriteLock
、QSemaphore
。 - 跨线程通信优先使用信号槽(自动排队或阻塞)。
- 使用
四、事件处理与绘图
- 事件循环(Event Loop)
- 问:解释Qt的事件处理流程。
- 答:
- 事件由
QApplication
进入事件队列。 QEventLoop
逐一分发事件(如鼠标、键盘、定时器事件)。- 可重写
event(QEvent*)
或特定事件处理器(如mousePressEvent()
)。
- 事件由
- 绘图机制
- 问:如何使用QPainter进行自定义绘图?
- 答:
- 在
paintEvent()
中使用QPainter
绘制图形。 - 双缓冲技术:通过
QPixmap
作为临时画布避免闪烁。
- 在
五、网络与模型视图编程
- TCP/UDP通信
- 问:如何实现Qt下的TCP客户端?
- 答:
QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 1234);
connect(socket, &QTcpSocket::readyRead, [=](){QByteArray data = socket->readAll();// 处理数据
});
- 模型/视图框架
- 问:
QAbstractItemModel
的作用是什么? - 答:
- 提供数据接口供视图(如
QListView
)显示,分离数据与UI。 - 需实现
rowCount()
,data()
,setData()
等方法。
- 提供数据接口供视图(如
- 问:
六、实际场景问题
- 调试与性能优化
- 问:如何检测Qt程序的内存泄漏?
- 答:
- 使用工具:
Valgrind
(Linux)、VLD
(Windows)、Qt内置的qDebug()
输出对象树。 - 检查未正确父对象化的
QObject
。
- 使用工具:
- 跨平台适配
- 问:Qt如何实现跨平台?
- 答:
- 抽象底层API(如OpenGL、DirectX),提供统一接口。
- 使用
Q_OS_WIN
、Q_OS_LINUX
等宏处理平台差异。
示例代码:信号槽连接
// 声明
class Worker : public QObject {Q_OBJECT
public slots:void doWork() { /* ... */ }
};// 连接
QThread thread;
Worker worker;
worker.moveToThread(&thread);
connect(&thread, &QThread::started, &worker, &Worker::doWork);
thread.start();
高频考点总结
- 信号槽的5种连接方式:
Qt::AutoConnection
(默认)、DirectConnection
、QueuedConnection
等。 - 事件过滤器:
installEventFilter()
拦截处理事件。 - 国际化:使用
tr()
包裹字符串,配合.ts
文件翻译。 - 样式表(QSS):类似CSS,定制控件外观。