Qt 框架深度解析与性能优化实践文档
1. 概述
1.1 文档目的
本文档系统总结 Qt 框架核心机制的深度理解与实践经验,涵盖:
- Qt 核心机制原理(信号槽、事件循环、对象模型)
- Qt 内存管理机制与内存泄漏预防
- 代码质量与可维护性实践
- 大数据文件加载性能优化方案
- 框架级问题解决典型案例
2. Qt 核心机制深度解析
2.1 信号槽机制
2.1.1 原理深度理解
核心架构:
信号发送 → QMetaObject::activate() → 连接查找 → 槽函数调用
关键组件:
| 组件 | 作用 | 源码位置 |
|---|---|---|
QMetaObject |
存储 meta-data(信号、槽、属性) | qmetaobject.cpp |
QObjectPrivate::ConnectionList |
存储连接关系 | qobject.cpp |
QMetaCallEvent |
跨线程信号事件 | qobject.cpp |
连接类型对比:
| 类型 | 触发方式 | 适用场景 | 性能 |
|---|---|---|---|
DirectConnection |
直接调用(同步) | 同线程、实时响应 | 最高 |
QueuedConnection |
事件队列(异步) | 跨线程、避免阻塞 | 中等 |
AutoConnection |
自动选择 | 默认推荐 | 动态 |
BlockingQueuedConnection |
阻塞等待 | 跨线程同步返回 | 最慢 |
2.1.2 深入源码分析
信号发射流程:
cpp
// moc 生成的信号函数
void MyObject::valueChanged(int newValue)
{
// 调用 QMetaObject::activate
QMetaObject::activate(this, &staticMetaObject,
0, // 信号索引
&newValue);
}
// activate 内部逻辑
void QMetaObject::activate(QObject *sender, ...)
{
// 1. 查找连接列表
QObjectPrivate::ConnectionList *list =
sender->d_func()->connectionLists;
// 2. 遍历连接
for (Connection *c : list) {
if (c->connectionType == Qt::QueuedConnection) {
// 跨线程:创建 QMetaCallEvent,投递到事件队列
QMetaCallEvent *event = new QMetaCallEvent(...);
QCoreApplication::postEvent(receiver, event);
} else {
// 同线程:直接调用槽函数
c->call(sender, argv);
}
}
}
关键发现:
- 信号槽连接存储在
QObjectPrivate::ConnectionList - 连接查找基于信号索引,O(n) 复杂度
- 跨线程信号通过
QMetaCallEvent实现,依赖事件循环 - 连接断开时需从列表移除,避免悬空指针
2.1.3 框架级问题案例
案例 1:跨线程信号槽连接失效
| 项目 | 内容 |
|---|---|
| 现象 | 子线程发射信号,主线程槽函数不触发 |
| 根因 | 子线程未启动事件循环,QMetaCallEvent 无法投递和分发 |
| 分析过程 | 1. 检查连接类型确认为 AutoConnection 2. 分析线程结构发现子线程仅有 QThread 对象 3. 深入源码定位 QThread::run() 未调用 exec() 4. 确认事件循环缺失导致事件无法处理 |
| 解决方案 | 在 QThread::run() 中调用 exec() 启动事件循环 |
| 效果 | 问题彻底解决,槽函数正常触发 |
cpp
// 问题代码
class MyThread : public QThread {
void run() override {
// 只执行任务,未启动事件循环
doWork();
}
};
// 解决方案
class MyThread : public QThread {
void run() override {
doWork();
exec(); // 启动事件循环,处理跨线程信号
}
};
案例 2:信号槽连接内存泄漏
| 项目 | 内容 |
|---|---|
| 现象 | 频繁创建销毁对象,内存持续增长不释放 |
| 根因 | 连接未正确断开,ConnectionList 中残留悬空连接 |
| 分析过程 | 1. 内存分析工具定位泄漏点在 QObjectPrivate 2. 检查代码发现 connect() 后未调用 disconnect() 3. 深入源码确认连接列表未清理导致引用残留 |
| 解决方案 | 对象销毁前显式 disconnect() 或使用 Qt::UniqueConnection |
| 效果 | 内存泄漏问题解决,内存正常释放 |
cpp
// 问题代码
void createObject() {
MyObject *obj = new MyObject();
connect(obj, &MyObject::signal, this, &Main::slot);
obj->deleteLater(); // 连接未断开,残留悬空指针
}
// 解决方案 1:显式断开
void createObject() {
MyObject *obj = new MyObject();
QMetaObject::Connection conn = connect(obj, &MyObject::signal,
this, &Main::slot);
obj->deleteLater();
connect(obj, &QObject::destroyed, [conn]() {
disconnect(conn);
});
}
// 解决方案 2:UniqueConnection(自动管理)
connect(obj, &MyObject::signal, this, &Main::slot,
Qt::UniqueConnection); // 重复连接自动忽略,对象销毁自动清理
2.2 事件循环机制
2.2.1 原理深度理解
核心架构:
事件源 → 事件队列 → QCoreApplication::exec() → 事件分发 → 处理器
关键流程:
cpp
int QCoreApplication::exec()
{
QEventLoop eventLoop;
return eventLoop.exec(); // 进入循环
}
int QEventLoop::exec()
{
while (!quit) {
// 1. 等待事件
waitForMoreEvents();
// 2. 从队列取事件
QEvent *event = QCoreApplication::instance()->d_func()->
postedEvents->takeFirst();
// 3. 分发事件
QCoreApplication::sendEvent(receiver, event);
}
}
事件类型与优先级:
| 类型 | 来源 | 优先级 | 处理方式 |
|---|---|---|---|
QMetaCallEvent |
跨线程信号槽 | 高 | sendEvent |
QTimerEvent |
定时器 | 中 | sendEvent |
QPaintEvent |
绘制请求 | 低 | sendEvent |
QInputEvent |
用户输入 | 高 | sendEvent |
2.2.2 深入源码分析
事件投递机制:
cpp
void QCoreApplication::postEvent(QObject *receiver, QEvent *event,
int priority)
{
// 1. 获取事件队列
QThreadData *data = receiver->d_func()->threadData;
QPostEventList *list = data->postEventList;
// 2. 按优先级插入队列
list->append(event); // 或按优先级位置插入
// 3. 通知事件循环有新事件
data->eventDispatcher->wakeUp();
}
关键发现:
- 每个线程有独立事件队列(
QThreadData::postEventList) - 事件优先级影响队列顺序
wakeUp()通知事件循环跳出等待sendEvent直接处理,postEvent队列投递
2.2.3 框架级问题案例
案例:事件循环阻塞导致界面卡死
| 项目 | 内容 |
|---|---|
| 现象 | 执行长时间任务时界面完全卡死,无响应 |
| 根因 | 同步耗时操作阻塞事件循环,事件无法分发 |
| 分析过程 | 1. 界面卡死时 CPU 占用率高 2. 代码分析发现同步网络请求在主线程 3. 深入源码确认 exec() 循环被阻塞无法返回 |
| 解决方案 | 1. 异步化耗时操作(线程/异步 API) 2. 添加事件处理窗口(processEvents) |
| 效果 | 界面保持响应,用户体验改善 |
cpp
// 问题代码
void loadData() {
QNetworkReply *reply = manager.get(request);
// 同步等待,阻塞事件循环
while (!reply->isFinished()) {
QThread::msleep(100); // 阻塞!事件循环无法执行
}
processData(reply);
}
// 解决方案 1:异步化
void loadData() {
QNetworkReply *reply = manager.get(request);
connect(reply, &QNetworkReply::finished, [reply]() {
processData(reply);
reply->deleteLater();
});
}
// 解决方案 2:事件处理窗口(临时)
void loadData() {
QNetworkReply *reply = manager.get(request);
while (!reply->isFinished()) {
// 处理积压事件,保持界面响应
QCoreApplication::processEvents();
QThread::msleep(50);
}
processData(reply);
}
2.3 对象模型
2.3.1 原理深度理解
核心架构:
QObject → QObjectPrivate (d-pointer) → QMetaObject (meta-data)
关键设计:
| 设计 | 作用 | 优势 |
|---|---|---|
| d-pointer 模式 | 实现细节隐藏在 QObjectPrivate | 二进制兼容、ABI 稳定 |
| QMetaObject | 存储 meta-data(信号、槽、属性) | 动态类型、反射能力 |
| 父子对象树 | 父对象销毁时自动销毁子对象 | 内存管理自动化 |
| 对象生命周期 | deleteLater() 延迟销毁 | 安全销毁、避免悬空 |
2.3.2 深入源码分析
父子对象树实现:
cpp
void QObjectPrivate::setParent(QObject *parent)
{
// 1. 从旧父对象移除
if (oldParent) {
oldParent->d_func()->children.removeOne(this);
}
// 2. 加入新父对象子列表
if (newParent) {
newParent->d_func()->children.append(this);
}
// 3. 更新父指针
parent = newParent;
}
void QObject::~QObject()
{
// 1. 销毁所有子对象
for (QObject *child : children) {
delete child; // 自动递归销毁子对象树
}
// 2. 从父对象移除自己
if (parent) {
parent->d_func()->children.removeOne(this);
}
// 3. 断开所有连接
disconnect();
}
关键发现:
- 子对象列表存储在
QObjectPrivate::children - 父对象销毁时自动 delete 所有子对象
deleteLater()投递QEvent::DeferredDelete事件- 对象销毁顺序:子对象 → 断开连接 → 从父对象移除
2.3.3 框架级问题案例
案例:父子对象树误用导致崩溃
| 项目 | 内容 |
|---|---|
| 现象 | 程序退出时崩溃,访问已销毁对象 |
| 根因 | 手动 delete 子对象后,父对象再次尝试 delete |
| 分析过程 | 1. 崩溃定位在 QObject::~QObject() 2. 代码分析发现手动 delete 子对象 3. 深入源码确认父对象析构时二次 delete |
| 解决方案 | 让 Qt 自动管理子对象,避免手动 delete |
| 效果 | 崩溃问题彻底解决 |
cpp
// 问题代码
QWidget *parent = new QWidget();
QPushButton *child = new QPushButton(parent); // 指定父对象
// 手动 delete 子对象(错误!)
delete child; // parent 的 children 列表仍引用 child
// 程序退出时,parent 析构再次 delete child → 崩溃!
// 解决方案 1:不手动 delete,让父对象管理
QWidget *parent = new QWidget();
QPushButton *child = new QPushButton(parent);
// 不调用 delete child,由 parent 自动管理
// 解决方案 2:不指定父对象,手动管理
QPushButton *child = new QPushButton(); // 无父对象
// 手动管理生命周期
delete child; // 安全
// 解决方案 3:使用 deleteLater() 安全销毁
child->deleteLater(); // 投递延迟删除事件,避免立即销毁
3. Qt 内存管理机制与内存泄漏预防
3.1 内存管理机制深度理解
3.1.1 核心机制
| 机制 | 说明 | 适用场景 |
|---|---|---|
| 父子对象树 | 父对象自动销毁子对象 | Widget、QObject 子类 |
| deleteLater() | 延迟安全销毁 | 事件循环内对象 |
| QSharedPointer | 智能指针引用计数 | 非 QObject 对象 |
| QScopedPointer | 独占智能指针 | 局部作用域对象 |
| QObjectData | d-pointer 内存布局 | 内部实现隐藏 |
3.1.2 内存泄漏常见原因
| 原因 | 说明 | 预防方法 |
|---|---|---|
| 连接未断开 | connect 后对象销毁未 disconnect | Qt::UniqueConnection |
| 循环引用 | 父子对象互相引用 | 合理设计对象树 |
| 事件未处理 | postEvent 后对象销毁事件残留 | 及时清理事件队列 |
| 手动管理错误 | 混用 Qt 自动管理和手动管理 | 统一管理策略 |
| 静态对象引用 | 静态变量引用动态对象 | 避免静态引用动态 |
3.1.3 内存泄漏排查方法论
排查流程:
现象观察(内存增长) → 工具分析(Visual Leak Detector) →
源码定位(泄漏点) → 根因分析(Qt 机制误用) →
方案设计(修正管理方式) → 验证效果(内存稳定)
工具推荐:
| 工具 | 功能 | 适用平台 |
|---|---|---|
| Visual Leak Detector | 检测 C++ 内存泄漏 | Windows |
| Valgrind | 内存错误检测 | Linux |
| Qt Creator 内存分析 | Qt 对象泄漏检测 | 跨平台 |
| Heaptrack | 堆内存追踪 | Linux |
3.2 内存泄漏预防最佳实践
3.2.1 对象生命周期管理原则
cpp
// ✅ 原则 1:统一管理方式
// Qt 管理的对象:指定父对象,不手动 delete
QWidget *widget = new QWidget(parentWidget);
// 手动管理的对象:不指定父对象,用智能指针
QScopedPointer<MyData> data(new MyData());
// ✅ 原则 2:安全销毁时机
// 立即销毁(确定无引用)
delete object; // 仅用于无父对象、无连接的对象
// 延迟销毁(事件循环内)
object->deleteLater(); // 推荐,安全
// ✅ 原则 3:连接管理
// 使用 UniqueConnection 自动管理
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::UniqueConnection);
// 或记录连接,销毁时断开
QMetaObject::Connection conn = connect(...);
connect(receiver, &QObject::destroyed, [conn]() {
disconnect(conn);
});
3.2.2 典型内存泄漏案例
案例:信号槽连接泄漏
| 项目 | 内容 |
|---|---|
| 代码 | 频繁创建临时对象并 connect,未断开连接 |
| 泄漏原因 | 连接列表残留引用,对象销毁后连接仍存在 |
| 解决方案 | 使用 Qt::UniqueConnection 或显式 disconnect |
cpp
// 问题代码
void processFile(QString path) {
FileProcessor *processor = new FileProcessor();
connect(processor, &FileProcessor::finished,
this, &Main::onFinished);
processor->process(path);
processor->deleteLater(); // 对象销毁,但连接残留
}
// 内存持续增长:每次调用创建新连接,销毁后残留
// 解决方案
void processFile(QString path) {
FileProcessor *processor = new FileProcessor();
connect(processor, &FileProcessor::finished,
this, &Main::onFinished, Qt::UniqueConnection);
processor->process(path);
processor->deleteLater();
}
// UniqueConnection:对象销毁时自动清理连接
4. 代码质量与可维护性实践
4.1 代码质量标准
4.1.1 Qt 代码规范
| 规范项 | 要求 | 示例 |
|---|---|---|
| 命名规范 | 类名 PascalCase,函数 camelCase,变量 camelCase | MyClass, getValue(), m_data |
| 信号槽命名 | 信号名描述事件,槽名描述动作 | valueChanged(), onValueChanged() |
| 成员变量 | 私有成员前缀 m_ 或 _ | m_name, _value |
| 常量命名 | 全大写,下划线分隔 | MAX_SIZE, DEFAULT_VALUE |
| 注释规范 | 关键逻辑注释,API 文档注释 | /** @brief 获取数据 */ |
4.1.2 可维护性原则
| 原则 | 说明 | 实践方法 |
|---|---|---|
| 低耦合 | 模块间依赖最小化 | 接口隔离、依赖注入 |
| 高内聚 | 模块内功能紧密相关 | 功能分组、职责单一 |
| 代码复用 | 提取公共组件 | 工具类、基础组件 |
| 文档完善 | 关键逻辑有文档 | 设计文档、API 文档 |
| 测试覆盖 | 核心功能有测试 | 单元测试、集成测试 |
4.2 代码质量提升实践
4.2.1 模块化设计
cpp
// ✅ 模块化设计示例
// 1. 接口抽象
class IDataProcessor {
public:
virtual ~IDataProcessor() = default;
virtual void process(const QByteArray& data) = 0;
virtual QByteArray getResult() = 0;
};
// 2. 实现分离
class Hdf5DataProcessor : public IDataProcessor {
public:
void process(const QByteArray& data) override { /* ... */ }
QByteArray getResult() override { /* ... */ }
};
// 3. 工厂模式
class DataProcessorFactory {
public:
static IDataProcessor* create(FileType type) {
switch (type) {
case HDF5: return new Hdf5DataProcessor();
case JSON: return new JsonDataProcessor();
default: return nullptr;
}
}
};
// 4. 依赖注入
class DataManager {
IDataProcessor *m_processor;
public:
void setProcessor(IDataProcessor *processor) {
m_processor = processor;
}
void processFile(QString path) {
QByteArray data = readFile(path);
m_processor->process(data);
}
};
4.2.2 代码评审要点
| 评审项 | 检查内容 | 工具 |
|---|---|---|
| 命名规范 | 命名是否符合 Qt 标准 | 人工检查 |
| 内存管理 | 对象生命周期是否正确 | VLD、静态分析 |
| 信号槽连接 | 连接是否正确、是否泄漏 | 代码审查 |
| 线程安全 | 多线程访问是否安全 | 静态分析 |
| 异常处理 | 错误处理是否完整 | 代码审查 |
| 性能影响 | 是否有性能瓶颈 | 性能测试 |
5. 大数据文件加载性能优化方案
5.1 性能瓶颈分析
5.1.1 问题背景
| 项目 | 内容 |
|---|---|
| 需求 | 加载 500MB+ HDF5 科学数据文件 |
| 问题 | 加载耗时 30 秒,界面卡死,用户体验极差 |
| 影响 | 客户投诉,影响产品竞争力 |
5.1.2 瓶颈定位方法
性能分析流程:
用户体验反馈 → 性能测试量化 → 瓶颈定位工具 →
根因分析 → 方案设计 → 实施验证 → 效果对比
工具与方法:
| 方法 | 工具 | 发现问题 |
|---|---|---|
| 时间测量 | QElapsedTimer | 定位耗时函数 |
| 性能剖析 | Qt Creator Profiler | 定位热点代码 |
| 内存分析 | Heaptrack | 定位内存瓶颈 |
| 源码分析 | Qt 源码阅读 | 理解底层机制 |
5.1.3 瓶颈根因
| 瓶颈 | 根因 | 占用时间 |
|---|---|---|
| 全量加载 | 一次性读取全部数据到内存 | 15秒 |
| 数据解析 | HDF5 解析开销大 | 8秒 |
| UI 更新 | 大量数据渲染阻塞 | 5秒 |
| 内存拷贝 | Qt 容器频繁拷贝 | 2秒 |
5.2 优化方案设计
5.2.1 流式加载架构
核心思路:分块读取 + 增量解析 + 增量渲染 + 内存池管理
cpp
// 流式加载架构设计
class StreamDataLoader : public QObject {
Q_OBJECT
public:
// 配置参数
struct Config {
int chunkSize = 10 * 1024 * 1024; // 10MB 每块
int maxMemory = 100 * 1024 * 1024; // 100MB 内存上限
int threadCount = 2; // 解析线程数
};
// 启动加载
void loadFile(QString path, Config config) {
m_filePath = path;
m_config = config;
// 启动读取线程
m_readThread = new ReadThread(this);
m_parseThreads = createParseThreads(config.threadCount);
connect(m_readThread, &ReadThread::chunkReady,
this, &StreamDataLoader::onChunkReady);
m_readThread->start();
}
signals:
void progressChanged(int percent);
void dataReady(QVector<DataChunk> chunks);
void loadFinished();
private:
// 读取线程:分块读取文件
class ReadThread : public QThread {
void run() override {
QFile file(m_path);
file.open(QIODevice::ReadOnly);
qint64 totalSize = file.size();
qint64 bytesRead = 0;
while (!file.atEnd()) {
// 读取一个块
QByteArray chunk = file.read(m_chunkSize);
bytesRead += chunk.size();
// 发射块数据
emit chunkReady(chunk, bytesRead, totalSize);
// 等待解析线程处理(避免内存积压)
m_semaphore.acquire();
}
}
};
// 解析线程池:并行解析数据块
class ParseThread : public QThread {
void run() override {
while (m_running) {
// 从队列获取待解析块
QByteArray chunk = m_queue->dequeue();
if (chunk.isEmpty()) break;
// 解析 HDF5 数据
QVector<DataItem> items = parseHdf5Chunk(chunk);
// 发射解析结果
emit parseReady(items);
// 通知读取线程继续
m_semaphore->release();
}
}
};
// 内存池管理:限制内存使用
class MemoryPool {
QVector<QByteArray> m_chunks;
int m_maxSize;
bool tryAllocate(int size) {
if (currentSize() + size > m_maxSize) {
// 释放旧数据
releaseOldest();
}
return true;
}
};
};
5.2.2 增量渲染优化
cpp
// 增量渲染策略
class IncrementalRenderer : public QObject {
Q_OBJECT
public:
void appendData(const QVector<DataItem>& items) {
// 添加到渲染队列
m_renderQueue.append(items);
// 启动定时器增量渲染
if (!m_renderTimer.isActive()) {
m_renderTimer.start(16); // 16ms,保持 60fps
}
}
private slots:
void onRenderTimeout() {
// 每帧渲染固定数量(避免阻塞)
int itemsPerFrame = 100;
for (int i = 0; i < itemsPerFrame && !m_renderQueue.isEmpty(); ++i) {
DataItem item = m_renderQueue.takeFirst();
renderItem(item);
}
// 队列空则停止
if (m_renderQueue.isEmpty()) {
m_renderTimer.stop();
emit renderFinished();
}
// 更新进度
emit progressChanged(calculateProgress());
}
private:
QTimer m_renderTimer;
QVector<DataItem> m_renderQueue;
};
5.2.3 内存管理优化
cpp
// 内存管理策略
class SmartMemoryManager {
public:
// LRU 缓存策略
template<typename T>
class LruCache {
QMap<QString, QSharedPointer<T>> m_cache;
QStringList m_accessOrder; // 访问顺序
int m_maxSize;
QSharedPointer<T> get(QString key) {
if (m_cache.contains(key)) {
// 更新访问顺序
m_accessOrder.removeAll(key);
m_accessOrder.append(key);
return m_cache[key];
}
return nullptr;
}
void put(QString key, QSharedPointer<T> data) {
// 超出限制则释放最久未访问
while (m_cache.size() >= m_maxSize) {
QString oldest = m_accessOrder.takeFirst();
m_cache.remove(oldest);
}
m_cache[key] = data;
m_accessOrder.append(key);
}
};
// 内存压力监控
void monitorMemoryPressure() {
qint64 currentUsage = getMemoryUsage();
qint64 threshold = 80 * 1024 * 1024; // 80MB
if (currentUsage > threshold) {
// 释放低优先级数据
releaseLowPriorityData();
// 通知用户
emit memoryPressureWarning(currentUsage);
}
}
};
5.3 优化效果对比
5.3.1 性能数据对比
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 加载时间(500MB) | 30秒 | 5秒 | 6倍 |
| 内存峰值 | 500MB | 100MB | 5倍降低 |
| 界面响应 | 卡死 | 流畅 | 显著改善 |
| 用户体验评分 | 60分 | 90分 | 30分提升 |
5.3.2 技术方案总结
| 方案 | 效果 | 实现复杂度 |
|---|---|---|
| 流式加载 | 加载时间降低 6 倍 | 高 |
| 增量渲染 | 界面保持流畅 | 中 |
| 内存池管理 | 内存峰值降低 5 倍 | 中 |
| 并行解析 | 解析效率提升 2 倍 | 高 |
| LRU 缓存 | 减少重复加载 | 中 |
6. 框架级问题解决典型案例
6.1 问题解决方法论
问题现象 → 日志分析 → 源码定位 → 根因确认 → 方案设计 →
实施验证 → 效果评估 → 知识沉淀 → 团队分享
6.2 典型案例汇总
| 案例 | 问题类型 | 解决方法 | 效果 |
|---|---|---|---|
| 跨线程信号失效 | 框架机制误用 | 启动事件循环 | 问题彻底解决 |
| 事件循环阻塞 | 同步操作阻塞 | 异步化改造 | 界面恢复响应 |
| 父子对象崩溃 | 内存管理错误 | 统一管理策略 | 崩溃消失 |
| 信号槽内存泄漏 | 连接管理错误 | UniqueConnection | 内存稳定 |
| 大数据加载性能 | 性能瓶颈 | 流式加载架构 | 性能提升 6 倍 |
| Qt 6 迁移兼容性 | API 变化 | 系统性迁移方案 | 成功迁移 |
7. 最佳实践总结
7.1 Qt 核心机制使用原则
| 原则 | 说明 |
|---|---|
| 信号槽 | 跨线程用 QueuedConnection,避免循环连接 |
| 事件循环 | 长任务异步化,必要时用 processEvents |
| 对象树 | 统一管理方式,避免混用自动和手动管理 |
| 内存管理 | Qt 管理的对象不手动 delete,智能指针管理非 QObject |
7.2 性能优化原则
| 原则 | 说明 |
|---|---|
| 流式处理 | 大数据分块处理,避免全量加载 |
| 异步化 | 耗时操作异步执行,避免阻塞主线程 |
| 内存控制 | 设置内存上限,及时释放不常用数据 |
| 增量渲染 | 分帧渲染,保持界面流畅 |
7.3 代码质量原则
| 原则 | 说明 |
|---|---|
| 模块化 | 低耦合高内聚,接口抽象 |
| 规范化 | 遵循 Qt 代码规范,命名统一 |
| 文档化 | 关键逻辑有文档,API 有说明 |
| 测试化 | 核心功能有测试,回归验证 |
8. 附录
8.1 Qt 源码关键位置
| 模块 | 源码文件 | 关键内容 |
|---|---|---|
| 信号槽 | qobject.cpp |
activate、connect、disconnect |
| 事件循环 | qcoreapplication.cpp |
exec、postEvent、sendEvent |
| 对象模型 | qobject.cpp |
析构、父子关系、d-pointer |
| 内存管理 | qsharedpointer.h |
智能指针实现 |