Qt 优化技巧
cpp connect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue, Qt::DirectConnection);
### 1.2 避免临时对象和复杂参数
尽量传递指针或常量引用,而不是按值传递大对象。
```cpp
// 不推荐
signals: void dataReady(QVector<int> data);
// 推荐
signals: void dataReady(const QVector<int> &data);
1.3 减少信号发射频率
使用定时器或合并机制,避免高频信号引发连锁更新。
// 使用 QTimer 合并连续更新
QTimer::singleShot(0, this, [this](){ emit aggregatedUpdate(); });
1.4 使用 lambda 替代槽函数(在简单场景下)
lambda 可以减少需要声明的槽函数,并且通常会被内联优化。
connect(button, &QPushButton::clicked, this, [this](){ /* 简单操作 */ });
2. 界面绘制优化
2.1 合理使用 QWidget 和 QML
- 对于静态或传统桌面 UI,QWidget 性能更好。
- 对于动态特效和现代 UI,QML 配合硬件加速更佳,但要避免过度绑定。
2.2 减少不必要的重绘
- 重写
paintEvent()时只绘制变化区域。 - 使用
setAttribute(Qt::WA_OpaquePaintEvent)禁止背景重绘(适用于不透明控件)。 - 调用
update()而非repaint(),让 Qt 合并多次绘制。
2.3 启用双缓冲
默认情况下,Qt 会为大多数控件开启双缓冲,但自定义控件可能需要手动设置:
setAttribute(Qt::WA_PaintOnScreen, false); // 确保使用双缓冲
2.4 视口裁剪与脏区域管理
在绘制大画布时主动裁剪:
void MyWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setClipRect(event->rect());
// 只绘制裁剪区域内的内容
}
2.5 使用 QPixmap 缓存静态内容
将不变的背景或图层预渲染到 QPixmap,在 paintEvent 中直接绘制 pixmap。
QPixmap cachePixmap;
// 绘制一次到 cachePixmap,之后只贴图
painter.drawPixmap(0, 0, cachePixmap);
3. 模型/视图性能优化
3.1 自定义模型中的数据访问
- 实现
data()和setData()时保证 O(1) 复杂度。 - 避免在
data()中进行昂贵计算,使用缓存机制。
3.2 使用 canFetchMore() 和 fetchMore()
处理海量数据时,分批加载:
bool MyModel::canFetchMore(const QModelIndex &parent) const {
return !m_allDataFetched;
}
void MyModel::fetchMore(const QModelIndex &parent) {
// 加载下一批数据,然后调用 beginInsertRows/endInsertRows
}
3.3 视图优化设置
- 启用
setUniformRowHeights(true)提高 QTableView 性能。 - 对于大列表,关闭
setEditTriggers避免意外创建编辑器。 - 使用
QAbstractItemView::ScrollPerPixel模式减少缩放计算。
4. 多线程与并发
4.1 将耗时任务移出主线程
使用 QThread、QtConcurrent::run 或 QThreadPool 处理计算密集工作。
QtConcurrent::run([this](){
// 重计算操作
}).then(this, [this](Result r){ /*更新 UI*/ });
4.2 避免直接跨线程调用 UI 方法
只能通过信号-槽(自动排队)或 QMetaObject::invokeMethod 更新 UI。
QMetaObject::invokeMethod(widget, "update", Qt::QueuedConnection);
4.3 线程负载均衡
将任务拆分成小块,使多核 CPU 充分利用。使用 QFutureSynchronizer 等待并发任务。
4.4 无锁容器与原子操作
当共享数据很简单时,可以使用 QAtomicInt、QAtomicPointer 等避免互斥锁开销。
5. 内存管理与容器优化
5.1 使用隐式共享和移动语义
Qt 的许多类(QString、QList、QImage 等)采用写时复制 (Copy-on-Write)。传递时尽量使用 const&,或利用 std::move。
QStringList bigList;
anotherList = std::move(bigList); // 避免深拷贝
5.2 预分配容器大小
避免频繁内存重新分配。
QVector<int> data;
data.reserve(10000);
// 批量添加元素
5.3 选择合适的容器
- 频繁头尾插入用
QList或QLinkedList(Qt 5 中 QList 已是优化数组)。 - 大量查找用
QHash。
5.4 尽早释放资源
使用 QScopedPointer 或 std::unique_ptr 管理动态分配,或利用父子对象机制。
5.5 避免内存碎片
对于大量小对象,考虑自定义内存池或使用 QObject 的父对象树一次性删除。
6. 启动与加载优化
6.1 延迟初始化
将非必要模块的初始化推迟到第一次使用。
6.2 使用 QSplashScreen 掩盖加载时间
用 splash 屏幕提升用户感知速度。
6.3 减少动态库依赖
链接不必要的 Qt 模块会增加加载时间。仅包含需要的模块(如 QT -= gui 用于纯控制台程序)。
6.4 资源文件优化
- 将大资源文件拆分成多个 .qrc 并懒加载。
- 使用
QResource::registerResource()动态加载资源包。
7. 事件循环与响应性
7.1 保持事件循环运行
阻止长时间操作主线程,否则界面卡死。将 CPU 密集型任务切片,定期调用 QCoreApplication::processEvents()(谨慎使用,推荐用线程)。
for (int i = 0; i < huge; ++i) {
// 处理一小片
if (i % 1000 == 0)
QCoreApplication::processEvents();
}
7.2 使用零延时定时器分段执行
让出控制权以保持界面响应。
QTimer::singleShot(0, this, &MyClass::nextChunk);
8. 网络与 I/O 优化
8.1 使用异步 I/O
- 使用
QNetworkAccessManager的非阻塞模式。 - 对本地文件大操作也用
QThread。
8.2 批量数据库操作
在 SQL 模块中,使用事务批量写入,避免逐条提交。
QSqlDatabase::database().transaction();
for (auto &record : records) {
// insert
}
QSqlDatabase::database().commit();