编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

    • Linux应用开发
    • QML基础入门

    • QT核心库实践

      • QT核心库实践
      • 核心功能基础
        • 1.1 基础类概念
          • 1.1.1 对象模型
          • 1.1.2 元对象系统
          • 1.1.3 看个案例
        • 1.2 QObject对象
          • 1.2.1 QObject说明
          • 1.2.2 树与父子关系
          • 1.2.3 对象名称与查找
          • 1.2.4 动态属性
          • 1.2.5 信号与槽
          • 1.2.6 事件处理
          • 1.2.7 线程支持
          • 1.2.8 注意事项
        • 1.3 元对象系统
          • 1.3.1 元对象说明
          • 1.3.2 元对象编译器
          • 1.3.3 反射(Reflection)
          • 1.3.4 动态调用
        • 1.4 关键宏与类
          • 1.4.1 Q_OBJECT
          • 1.4.2 QMetaObject
          • 1.4.3 Q_PROPERTY
          • 1.4.4 Q_INVOKABLE
          • 1.4.5 总结一下
        • 1.X QObject对象树与内存管理
          • 父对象析构顺序
          • 与QML的所有权
        • 1.Y QVariant类型系统
        • 1.Z 元对象编译器(moc)原理
        • 1.5 QMetaType 与自定义类型注册
          • 1.5.1 为什么需要注册类型
          • 1.5.2 QMetaType 内部机制
        • 1.6 QObject 生命周期管理
          • 1.6.1 deleteLater() 机制
          • 1.6.2 对象树析构顺序
        • 1.7 属性系统深入
          • 1.7.1 属性绑定(Qt 6)
          • 1.7.2 QEnum 和 QFlag
        • 1.8 综合案例
          • 案例:完整的数据管理对象
        • 1.9 速查表
      • 并发与多线程
      • 文件与IO系统
      • 日期与时间处理
      • 网络与序列化
      • QT事件系统
      • 信号与槽机制
      • 多媒体的应用
      • 容器类和算法
      • 高级编程技巧
    • Linux系统编程

    • 综合项目实战

  • IoT智能硬件开发

  • Apps
  • Linux应用开发
  • QT核心库实践
杨充
2025-08-25
目录

核心功能基础

# 01.核心功能基础

# 目录介绍

  • 1.1 基础类概念
    • 1.1.1 对象模型
    • 1.1.2 元对象系统
    • 1.1.3 看个案例
  • 1.2 QObject对象
    • 1.2.1 QObject说明
    • 1.2.2 树与父子关系
    • 1.2.3 对象名称与查找
    • 1.2.4 动态属性
    • 1.2.5 信号与槽
    • 1.2.6 事件处理
    • 1.2.7 线程支持
    • 1.2.8 注意事项
  • 1.3 元对象系统
    • 1.3.1 元对象说明
    • 1.3.2 元对象编译器
    • 1.3.3 反射(Reflection)
    • 1.3.4 动态调用
  • 1.4 关键宏与类
    • 1.4.1 Q_OBJECT
    • 1.4.2 QMetaObject
    • 1.4.3 Q_PROPERTY
    • 1.4.4 Q_INVOKABLE
    • 1.4.5 总结一下

# 1.1 基础类概念

Qt Core 是 Qt 框架的基础模块,提供了跨平台的 C++ 核心功能,不依赖于 GUI 组件。

Qt 的对象模型和元对象系统是 Qt 框架的核心特性之一,它们为 Qt 提供了强大的功能,如信号与槽机制、动态属性、反射等。

# 1.1.1 对象模型

功能:提供对象树管理、父子关系、对象生命周期管理。核心类:

  • QObject:支持对象树和父子关系。所有 Qt 对象的基类,提供信号槽机制、对象树管理、事件处理等核心功能。
  • QObject::parent()、QObject::children():访问父对象和子对象。

# 1.1.2 元对象系统

功能:提供运行时类型信息(RTTI)、信号与槽机制、动态属性系统等。核心类:

  • QObject:所有 Qt 对象的基类。
  • QMetaObject:存储类的元信息(如类名、信号、槽、属性等)。
  • QMetaMethod、QMetaProperty:用于访问方法和属性信息。
  • Q_GADGET:为非 QObject 类添加元对象能力
  • Q_PROPERTY:声明属性系统
  • Q_INVOKABLE:使方法可通过元对象系统调用

元对象系统,核心概念

  • Qt 使用元对象系统实现信号与槽、动态属性、反射等功能。元对象系统通过 moc(元对象编译器)生成额外的代码,扩展 C++ 的功能。

关键特性

  • Q_OBJECT 宏:在类声明中使用 Q_OBJECT 宏,启用元对象系统功能。
  • 动态属性:可以在运行时为对象添加属性。
  • 反射:可以在运行时获取对象的类名、方法、属性等信息。

# 1.1.3 看个案例

以下是一个简单的示例,展示了 Qt 对象模型和元对象系统的使用:

#include <QObject>
#include <QDebug>

class MyClass : public QObject {
    Q_OBJECT  // 启用元对象系统
    
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)   //属性

public:
    explicit MyClass(QObject *parent = nullptr) : QObject(parent), m_value(0) {}  //构造方法

    int value() const { return m_value; }
    void setValue(int value) {
        if (m_value != value) {
            m_value = value;
            emit valueChanged(m_value);   //发送信号
        }
    }

signals:    //信号
    void valueChanged(int value);

private:
    int m_value;
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    MyClass obj;
    qDebug() << obj.metaObject()->className();  // 输出 "MyClass"
    obj.setObjectName("MyObject");
    // 使用元对象系统动态调用槽函数
    QMetaObject::invokeMethod(&obj, "setValue", Q_ARG(int, 42));
    // 使用反射获取属性
    qDebug() << "Value:" << obj.property("value").toInt();
    return app.exec();
}

#include "main.moc"

# 1.2 QObject对象

# 1.2.1 QObject说明

QObject 是 Qt 框架中最基础的类,几乎所有 Qt 类都直接或间接继承自 QObject。

它提供了 Qt 对象模型的核心功能,如对象树管理、信号与槽机制、事件处理、动态属性等。

# 1.2.2 树与父子关系

对象树与父子关系:Qt 对象可以组织成树形结构,每个对象可以有一个父对象和多个子对象。

当父对象被销毁时,其所有子对象也会自动销毁。通过 QObject::setParent() 设置父对象,或通过 QObject::children() 获取子对象。

void setParent(QObject *parent);
QObject *parent() const;

# 1.2.3 对象名称与查找

每个 QObject 对象可以有一个唯一的名称(objectName),用于在对象树中查找。

使用 QObject::findChild() 或 QObject::findChildren() 查找对象。

QObject *findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<QObject *> findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;

# 1.2.4 动态属性

Qt 对象可以动态添加属性,这些属性可以在运行时查询和修改。使用 QObject::setProperty() 设置属性,QObject::property() 获取属性。

bool setProperty(const char *name, const QVariant &value);
QVariant property(const char *name) const;

# 1.2.5 信号与槽

Qt 的信号与槽机制是对象模型的核心特性,用于对象之间的通信。

信号是事件的通知,槽是事件的响应函数。使用 QObject::connect() 连接信号与槽。

static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method);

# 1.2.6 事件处理

QObject 提供了事件处理机制,可以重写 QObject::event() 或特定事件处理函数(如 QObject::timerEvent())来处理事件。

virtual bool event(QEvent *event);
virtual void timerEvent(QTimerEvent *event);

# 1.2.7 线程支持

QObject 可以在多线程中使用,但需要注意线程安全性。使用 QObject::moveToThread() 将对象移动到另一个线程。

void moveToThread(QThread *targetThread);

# 1.2.8 注意事项

  1. Q_OBJECT 宏: 所有使用信号与槽、动态属性等特性的类必须包含 Q_OBJECT 宏。
  2. 对象树管理: 使用对象树管理父子关系,避免内存泄漏。注意不要在栈上创建带有父对象的 QObject,因为父对象销毁时会尝试删除子对象。
  3. 线程安全:QObject 默认是线程不安全的,跨线程操作时需要使用 QMetaObject::invokeMethod() 或信号与槽。
  4. 信号与槽: 信号与槽的参数类型必须完全匹配,否则连接会失败。使用 Qt::QueuedConnection 实现跨线程的信号与槽连接。

# 1.3 元对象系统

元对象系统(Meta-Object System)是 Qt 实现对象模型的基础,它提供了以下功能:

# 1.3.1 元对象说明

每个 QObject 派生类都有一个对应的元对象,用于描述类的元信息(如类名、信号、槽、属性等)。

元对象由 moc(Meta-Object Compiler)生成,存储在 moc_*.cpp 文件中。

# 1.3.2 元对象编译器

moc 是 Qt 的工具,用于处理 Q_OBJECT 宏,生成元对象代码。在编译 Qt 项目时,moc 会自动处理包含 Q_OBJECT 宏的头文件。

# 1.3.3 反射(Reflection)

元对象系统支持反射,可以在运行时查询对象的类信息、信号、槽、属性等。使用 QMetaObject 类访问元对象信息。

# 1.3.4 动态调用

通过元对象系统,可以在运行时动态调用对象的信号、槽和属性。使用 QMetaObject::invokeMethod() 动态调用槽函数。

QMetaObject::invokeMethod 它通常用于跨线程调用、延迟执行或调用信号和槽。

函数原型

static bool QMetaObject::invokeMethod(
    QObject *object,                // 目标对象
    const char *method,             // 方法名
    Qt::ConnectionType type,        // 连接类型
    QGenericReturnArgument ret,     // 返回值
    QGenericArgument val0 = QGenericArgument(), // 参数 1
    QGenericArgument val1 = QGenericArgument(), // 参数 2
    QGenericArgument val2 = QGenericArgument(), // 参数 3
    QGenericArgument val3 = QGenericArgument(), // 参数 4
    QGenericArgument val4 = QGenericArgument(), // 参数 5
    QGenericArgument val5 = QGenericArgument(), // 参数 6
    QGenericArgument val6 = QGenericArgument(), // 参数 7
    QGenericArgument val7 = QGenericArgument(), // 参数 8
    QGenericArgument val8 = QGenericArgument(), // 参数 9
    QGenericArgument val9 = QGenericArgument()  // 参数 10
);

参数说明

  1. object:目标对象,即调用方法的对象。
  2. method:方法名,可以是信号、槽或普通成员函数。方法名需要以字符串形式提供,例如 "methodName"。
  3. type:连接类型,决定方法调用的方式。常用值:
  • Qt::AutoConnection:自动选择(默认)。
  • Qt::DirectConnection:直接调用(同步)。
  • Qt::QueuedConnection:队列调用(异步)。
  1. ret:返回值,使用 Q_RETURN_ARG 宏定义。
  2. val0 到 val9:方法参数,使用 Q_ARG 宏定义。

看一个案例,比如:

QMetaObject::invokeMethod(this, [=]() { DoOnPalmServiceWorkingStatus(palm_working_enable); }, Qt::QueuedConnection);
  • QMetaObject::invokeMethod:Qt 框架提供的函数,用于在对象上调用指定的方法。
  • this:当前对象的指针,表示调用方法的对象。
  • = { DoOnPalmServiceWorkingStatus(palm_working_enable); }:Lambda 函数,捕获当前作用域的变量(palm_working_enable),并调用 DoOnPalmServiceWorkingStatus 方法。
  • Qt::QueuedConnection:指定调用方式为队列连接(异步调用),即方法会在事件循环中稍后执行,而不是立即执行。

# 1.4 关键宏与类

# 1.4.1 Q_OBJECT

Q_OBJECT 是一个宏,用于启用 Qt 的元对象系统功能,包括信号与槽机制、动态属性、对象树管理等。所有使用元对象系统的类必须包含 Q_OBJECT 宏。该宏会触发 moc 生成元对象代码。

  • 启用信号与槽机制。
  • 支持动态属性(Q_PROPERTY)。
  • 支持元对象信息(如 metaObject())。
  • 支持对象树管理(如 parent() 和 children())。

注意事项

  • 使用 Q_OBJECT 的类必须继承自 QObject。
  • 如果使用 Q_OBJECT,必须运行 moc(元对象编译器)生成额外的代码。

# 1.4.2 QMetaObject

QMetaObject 是 Qt 元对象系统的核心类,提供了对类元信息的访问。它的作用:

  • 获取类的名称、父类、信号、槽、属性等信息。
  • 动态调用方法或访问属性。
#include <QObject>
#include <QMetaObject>
#include <QDebug>

class MyClass : public QObject {
    Q_OBJECT
public:
    MyClass(QObject *parent = nullptr) : QObject(parent) {}
    void doSomething() {
        qDebug() << "Doing something...";
    }
};

int main() {
    MyClass obj;
    // 获取元对象
    const QMetaObject *meta = obj.metaObject();
    // 获取类名
    qDebug() << "Class name:" << meta->className();
    // 获取父类名
    qDebug() << "Parent class name:" << meta->superClass()->className();
    return 0;
}

# 1.4.3 Q_PROPERTY

Q_PROPERTY 是一个宏,用于声明类的属性,支持属性绑定、动画、序列化等功能。

作用

  • 声明一个属性,并指定其读写方法、通知信号等。
  • 支持在 QML 中绑定属性。

用法

#include <QObject>
#include <QDebug>

class MyClass : public QObject {
    Q_OBJECT
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) // 声明属性
public:
    MyClass(QObject *parent = nullptr) : QObject(parent), m_value(0) {}

    int value() const { return m_value; } // 读方法
    void setValue(int value) { // 写方法
        if (m_value != value) {
            m_value = value;
            emit valueChanged(); // 发射通知信号
        }
    }

signals:
    void valueChanged(); // 通知信号

private:
    int m_value;
};

int main() {
    MyClass obj;
    // 使用属性
    obj.setValue(42);
    qDebug() << "Value:" << obj.value();

    return 0;
}

属性选项

  • READ:指定读方法。
  • WRITE:指定写方法。
  • NOTIFY:指定属性变化时的通知信号。
  • RESET:指定重置方法。
  • MEMBER:指定成员变量。
  • CONSTANT:声明为常量属性。
  • FINAL:禁止子类重写属性。

# 1.4.4 Q_INVOKABLE

Q_INVOKABLE 是一个宏,用于将方法标记为可通过元对象系统调用的方法。

作用:允许通过 QMetaObject::invokeMethod() 动态调用方法。支持在 QML 中调用方法。

1.跨线程调用,在 Qt 中,如果需要在非主线程中调用主线程的对象方法,可以使用 QMetaObject::invokeMethod 并指定 Qt::QueuedConnection。

QMetaObject::invokeMethod(
    targetObject,       // 目标对象
    "methodName",       // 方法名
    Qt::QueuedConnection // 异步调用
);

2.延迟调用,通过 Qt::QueuedConnection,可以实现延迟调用。

QMetaObject::invokeMethod(
    targetObject,
    "methodName",
    Qt::QueuedConnection
);

3.调用带参数的方法,如果方法需要参数,可以使用 Q_ARG 宏传递参数。

QMetaObject::invokeMethod(
    targetObject,
    "setValue",
    Qt::AutoConnection,
    Q_ARG(int, 42),     // 参数 1
    Q_ARG(QString, "Hello") // 参数 2
);

4.调用带返回值的方法,如果方法有返回值,可以使用 Q_RETURN_ARG 宏。

int result;
QMetaObject::invokeMethod(
    targetObject,
    "calculate",
    Qt::DirectConnection,
    Q_RETURN_ARG(int, result), // 返回值
    Q_ARG(int, 10),            // 参数 1
    Q_ARG(int, 20)             // 参数 2
);
qDebug() << "Result:" << result;

注意事项

  • Q_INVOKABLE 方法可以是任意返回类型和参数类型。
  • 如果方法有返回值,可以使用 Q_RETURN_ARG 获取返回值。

# 1.4.5 总结一下

  • Q_OBJECT:启用元对象系统功能。
  • QMetaObject:提供对类元信息的访问。
  • Q_PROPERTY:声明属性,支持绑定和通知。
  • Q_INVOKABLE:标记方法为可通过元对象系统调用。

# 1.X QObject对象树与内存管理

QObject的对象树是Qt核心内存机制——理解析构顺序才能避免野指针。

# 父对象析构顺序

一个父对象析构时,按子对象加入的逆序递归删除:

parent
  ├── child1 ← 最晚删除
  ├── child2
  └── child3 ← 最先删除

关键规则:

  • 父对象析构自动删除所有子对象——不需要手动管理
  • 手动delete子对象安全——会自动从父对象children中移除
  • deleteLater() 不立即析构——事件循环下个迭代才删除,跨线程安全

# 与QML的所有权

// C++创建→C++管理
auto* backend = new Backend();
engine.rootContext()->setContextProperty("backend", backend);
// QML销毁时不会delete backend——CppOwnership

// QML创建→JS GC管理
// QML: Backend { id: qmlB }
// C++不要delete qmlB
QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership);

# 1.Y QVariant类型系统

QVariant是Qt的"通用类型容器"——内部用union+type tag:

QVariant v;
v = 42;                  // typeId=Int, 存qint32
v = QString("hello");    // typeId=String, 存QString(COW)

// 自定义类型注册
struct SensorData { float temp; int press; };
Q_DECLARE_METATYPE(SensorData)
QVariant v = QVariant::fromValue(SensorData{23.5, 1013});

// 与QML桥接
Q_PROPERTY(QVariant config READ config)  // QML侧读var

# 1.Z 元对象编译器(moc)原理

编译时而非运行时反射:

.h → moc扫描Q_OBJECT → moc_*.cpp:
  staticMetaObject:
    stringData[]      ← 类名/属性名/方法名字符串
    propertyData[]    ← {name,type,notifySignalIdx,flags}
    methodData[]      ← {name,params,returnType}
  qt_static_metacall(idx, args) ← 按索引dispatch到真实C++方法

与Java反射对比:编译期静态表→函数指针 vs 运行时HashMap→Method.invoke() 嵌入式ARM上前者快10-50倍。


# 1.5 QMetaType 与自定义类型注册

# 1.5.1 为什么需要注册类型

Qt 的元类型系统需要知道如何构造、复制、析构自定义类型——否则 QVariant、信号槽参数传递、QDataStream 序列化都无法工作:

struct SensorData {
    float temperature;
    int pressure;
    QString unit;
};
// ❌ 默认情况下 SensorData 不能放入 QVariant
// ❌ 不能通过信号槽传递

// 注册步骤:
// ① 声明元类型
Q_DECLARE_METATYPE(SensorData)

// ② 【可选】注册比较器和流操作符
bool operator==(const SensorData& a, const SensorData& b) {
    return a.temperature == b.temperature && a.pressure == b.pressure;
}

QDataStream& operator<<(QDataStream& out, const SensorData& d) {
    return out << d.temperature << d.pressure << d.unit;
}

QDataStream& operator>>(QDataStream& in, SensorData& d) {
    return in >> d.temperature >> d.pressure >> d.unit;
}

// ③ 注册到元类型系统
qRegisterMetaType<SensorData>("SensorData");
// 现在可以了!
QVariant v = QVariant::fromValue(SensorData{23.5f, 1013, "hPa"});

# 1.5.2 QMetaType 内部机制

// QMetaType 内部维护一个类型注册表
struct QMetaType::Registry {
    // 类型 ID → 函数指针表
    // { ctor, dtor, copy, compare, load, save, ... }
    QHash<int, ConverterFunctions> converters;
};

// qRegisterMetaType 做的事:
// 1. 分配一个唯一的 typeId
// 2. 注册构造/析构/拷贝函数(模板推导)
// 3. 注册 QDataStream 操作符(如果已重载)
// 4. 注册比较操作符(如果已重载)

# 1.6 QObject 生命周期管理

# 1.6.1 deleteLater() 机制

deleteLater() 是 Qt 最精巧的内存管理之一:

obj->deleteLater();
// 不是立即 delete obj!
// 而是向当前线程的事件循环投递一个 DeferredDelete 事件
// 事件循环在下一个迭代中处理它 → 真正 delete obj

// 为什么这样做?
// 场景:在槽函数中删除自己
void MyWidget::onButtonClicked() {
    delete this;  // ❌ 之后访问成员变量 → 崩溃
}

void MyWidget::onButtonClicked() {
    deleteLater();  // ✅ 当前函数安全执行完,下一轮才 delete
    m_data->update();  // 这条语句仍可安全执行
}

deleteLater 注意事项:

  • 如果对象没有线程事件循环 → 立即 delete(退化为普通 delete)
  • 对象的父对象被删除时,子对象用普通 delete,不用 deleteLater
  • 可以重复调用——只会投递一次 DeferredDelete 事件

# 1.6.2 对象树析构顺序

上一节已详述,这里补充禁止的操作:

// ❌ 禁止 1:栈上创建有父对象的 QObject
void foo() {
    QObject parent;
    QObject child(&parent);  // child 的父对象是栈上对象
}  // parent 先析构 → 尝试 delete child → child 被 delete 两次!

// ❌ 禁止 2:两个对象互为父子
parent->setParent(child);
child->setParent(parent);  // Qt 会检测循环并警告

// ❌ 禁止 3:在构造函数中通过信号槽调用自己的槽
MyObject::MyObject() {
    connect(this, &MyObject::ready, this, &MyObject::onReady);
    emit ready();  // onReady 中访问未初始化的成员 → UB!
}

# 1.7 属性系统深入

# 1.7.1 属性绑定(Qt 6)

Qt 6 引入真正的属性绑定——类似于 QML 的行为:

class MyWidget : public QWidget {
    Q_OBJECT
    Q_PROPERTY(int width READ width WRITE setWidth BINDABLE bindableWidth)
    Q_PROPERTY(int area READ area STORED false)

public:
    MyWidget() {
        // 属性绑定——width 变化时 area 自动更新
        m_area.setBinding([this]() { return m_width * m_height; });
    }

    int width() const { return m_width; }
    void setWidth(int w) { m_width = w; }  // 旧式 setter
    QBindable<int> bindableWidth() { return &m_width; }

    int area() const { return m_area; }

private:
    QProperty<int> m_width{100};   // Qt 6 可绑定属性
    QProperty<int> m_height{100};
    QProperty<int> m_area{10000};  // 绑定到 width * height
};

// 使用
widget.property("width").setValue(200);
// → m_width = 200
// → area 绑定自动计算 → m_area = 20000

QProperty vs Q_PROPERTY 对比:

Q_PROPERTY(旧) QProperty(Qt 6)
绑定支持 仅 QML C++ + QML
变更通知 手动 emit signal 自动(内部)
类型安全 QVariant 转换 模板类型
性能 QVariant 装箱/拆箱 直接访问
与 QML 交互 ✅ ✅

# 1.7.2 QEnum 和 QFlag

class MyWidget : public QWidget {
    Q_OBJECT

public:
    enum Priority { Low, Normal, High, Critical };
    Q_ENUM(Priority)  // 注册到元对象 → QML 中可用 "MyWidget.Low"

    enum Option {
        OptionA = 0x01,
        OptionB = 0x02,
        OptionC = 0x04
    };
    Q_DECLARE_FLAGS(Options, Option)
    Q_FLAG(Options)   // 位或组合 → Qt 自动支持 "OptionA | OptionB"
};

Q_DECLARE_OPERATORS_FOR_FLAGS(MyWidget::Options)

# 1.8 综合案例

# 案例:完整的数据管理对象

class DataManager : public QObject {
    Q_OBJECT
    Q_PROPERTY(int count READ count NOTIFY countChanged)
    Q_PROPERTY(QString status READ status BINDABLE bindableStatus)

public:
    DataManager() {
        // 绑定 status 属性
        m_status.setBinding([this]() {
            if (m_data.size() > m_warningThreshold) return "Warning";
            if (m_data.isEmpty()) return "Empty";
            return QString("OK (%1)").arg(m_data.size());
        });
    }

    int count() const { return m_data.size(); }
    QString status() const { return m_status; }
    QBindable<QString> bindableStatus() { return &m_status; }

    Q_INVOKABLE void addData(const SensorData& data) {
        m_data.append(data);
        emit countChanged(m_data.size());
        // m_status 自动更新(绑定)—无需手动设置!
    }

    Q_INVOKABLE SensorData getData(int index) const {
        return m_data.at(index);
    }

    void setWarningThreshold(int t) {
        m_warningThreshold = t;
        m_status.markDirty();  // 标记脏,触发绑定重新计算
    }

signals:
    void countChanged(int count);

private:
    QList<SensorData> m_data;
    QProperty<int> m_count{0};
    QProperty<QString> m_status{"Empty"};
    int m_warningThreshold = 100;
};

# 1.9 速查表

宏/类 核心作用
Q_OBJECT 启用元对象系统(信号、槽、属性)
Q_GADGET 为非 QObject 类添加元对象能力
Q_PROPERTY 声明属性(READ/WRITE/NOTIFY/BINDABLE)
Q_INVOKABLE 标记方法可从 QML / 反射调用
Q_ENUM / Q_FLAG 向 QML 暴露枚举
Q_DECLARE_METATYPE 注册自定义类型
qRegisterMetaType<T>() 运行时注册类型(信号槽跨线程必须)
QMetaObject 访问类的元信息(方法、属性)
QMetaObject::invokeMethod 动态调用方法(跨线程安全)
QProperty<T> C++ 可绑定属性(Qt 6)
QBindable<T> 获取属性的可绑定接口
QVariant 通用类型容器
setProperty() / property() 动态属性读写
deleteLater() 延迟删除(事件循环下一轮)

核心基础铁律:

  1. 继承 QObject 的类必须包含 Q_OBJECT 宏——否则信号槽不工作
  2. 跨线程传递自定义类型必须调用 qRegisterMetaType<T>()
  3. 不要在构造函数中 emit 信号调用自己的槽——成员未初始化
  4. 栈上 QObject 不能有非栈上的子对象——析构顺序导致 double delete
  5. deleteLater() 要求线程有事件循环——否则立即 delete
  6. Qt 6 优先使用 QProperty<T> 代替旧式 Q_PROPERTY——类型安全 + 自动绑定
上次更新: 2026/07/12, 19:39:51
QT核心库实践
并发与多线程

← QT核心库实践 并发与多线程→

最近更新
01
audit
07-27
02
C++入门教程全章思考题汇编
07-24
03
12.技术团队建设能力
07-21
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式