Qt 的元對象系統(Meta-Object System),這是 Qt 框架最核心、最強大的特性之一。
1.什么是 Qt 的元對象系統?
Qt 的元對象系統(Meta-Object System)是 Qt 在標準 C++ 基礎上擴展的一套機制,它為 C++ 增加了:
信號與槽(Signals and Slots)
運行時類型信息(RTTI)
動態屬性系統(Dynamic Properties)
對象樹與對象生命周期管理
可翻譯字符串(tr())
枚舉與標志的反射支持
這一切都依賴于 Qt 的 元對象編譯器(moc, Meta-Object Compiler)。
元對象系統的核心組件
Q_OBJECT 宏 | 啟用元對象功能的“開關” |
moc(元對象編譯器) | 自動生成信號、槽、屬性等的膠水代碼 |
QObject 基類 | 所有支持元對象系統的類必須繼承它 |
signals / slots | 特殊關鍵字,用于聲明信號和槽 |
Q_PROPERTY | 聲明可在 QML 中訪問的屬性 |
示例: |
// person.h
#ifndef PERSON_H
#define PERSON_H#include <QObject>
#include <QString>class Person : public QObject // 必須繼承 QObject
{Q_OBJECT // ?? 必須有!這是元對象系統的“開關”// 聲明一個屬性Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)public:explicit Person(QObject *parent = nullptr);// GetterQString name() const { return m_name; }int age() const { return m_age; }public slots:void setName(const QString &name);void setAge(int age);signals:void nameChanged(const QString &name);void ageChanged(int age);// 自定義信號void greeted(const QString &message);private:QString m_name;int m_age;
};#endif // PERSON_H
// person.cpp
#include "person.h"
#include <QDebug>Person::Person(QObject *parent): QObject(parent), m_name("Unknown"), m_age(0)
{}void Person::setName(const QString &name)
{if (m_name != name) {m_name = name;emit nameChanged(m_name); // 發射信號}
}void Person::setAge(int age)
{if (m_age != age) {m_age = age;emit ageChanged(m_age);emit greeted("Hello, I'm " + m_name + ", " + QString::number(age) + " years old.");}
}
元對象系統如何工作?—— moc 的作用
當你編譯這個類時,Qt 的構建系統會:
運行 moc 工具 處理 person.h
生成一個中間文件:moc_person.cpp
這個文件包含:
信號的“發射函數”(如 nameChanged())
屬性系統的元數據(用于 QML)
槽的調用機制
RTTI 信息(metaObject())
你不需要手動寫這些代碼,moc 會自動生成。
元對象系統的五大核心功能詳解
1.信號與槽(Signals and Slots)
2.運行時類型信息(RTTI)
你可以動態查詢對象的類型和屬性:
const QMetaObject *meta = person.metaObject();
qDebug() << "Class name:" << meta->className();for (int i = 0; i < meta->propertyCount(); ++i) {QMetaProperty prop = meta->property(i);qDebug() << "Property:" << prop.name() << "Type:" << prop.typeName();
}
Class name: Person
Property: objectName Type: QString
Property: name Type: QString
Property: age Type: int
3.Q_PROPERTY:屬性系統(用于 QML)
Q_PROPERTY 讓 C++ 屬性可以在 QML 中使用。
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
在 QML 中:
import com.example.person 1.0Person {id: personname: "Alice"age: 30onNameChanged: console.log("Name changed to:", name)onAgeChanged: console.log("Age is now:", age)
}
4,動態屬性(Dynamic Properties)
動態屬性 = 你可以在運行時,給一個對象“貼便簽”,上面寫著一些信息,之后還能隨時查看或修改。
你可以在運行時添加屬性:
class Person : public QObject {Q_OBJECTQ_PROPERTY(QString name READ name) // 編譯時就知道有 name 屬性
public:QString name() const { return "Alice"; }
};
name 是類的一部分
寫代碼時就確定了
所有 Person 對象都有 name
Person alice;
alice.setProperty("age", 25); // 臨時加個 age
alice.setProperty("city", "Beijing"); // 臨時加個 city
alice.setProperty("isStudent", true); // 臨時加個 isStudent
age、city、isStudent 不是 Person 類定義的!
是你在程序運行時臨時貼上去的“便簽”
其他 Person 對象不一定有這些屬性
5,對象樹與內存管理
Qt 使用父子對象樹自動管理內存
```cpp
QObject *parent = new QObject;
QObject *child = new QObject(parent); // 設置父對象delete parent; // 自動 delete child
這避免了手動 delete 的麻煩,也防止內存泄漏。
總結
在 QML 中調用 C++ 方法時,為什么有些方法需要使用 Q_INVOKABLE 宏來聲明?這個問題涉及到 Qt 的元對象系統如何與 QML 進行交互的機制。
1.QML 和 C++ 的交互方式
Qt 提供了兩種主要的方式讓 QML 能夠調用 C++ 的功能:
通過 slots(槽函數):任何標記為 public slots: 的函數都可以被 QML 直接調用。
通過 Q_INVOKABLE:對于那些不是槽函數但你希望從 QML 調用的方法,可以使用 Q_INVOKABLE 來聲明它們
Q_INVOKABLE 則是一種標志,告訴 Qt 元對象系統這個函數可以從 QML 調用,但它不參與信號和槽的連接。